Skip to content

Commit

Permalink
Bump version to 11 (#110)
Browse files Browse the repository at this point in the history
* clear nonce after signing with the state machine

* inc major api semver since we had to change a function signature

* add test

* fix panic

* use assert_ne

* add lots of comments to the test
  • Loading branch information
xoloki authored Dec 19, 2024
1 parent 3689ec6 commit e3ab997
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wsts"
version = "10.0.0"
version = "11.0.0"
edition = "2021"
authors = ["Joey Yandle <[email protected]>"]
license = "Apache-2.0"
Expand Down
14 changes: 12 additions & 2 deletions src/state_machine/coordinator/fire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,8 @@ pub mod test {
fire::Coordinator as FireCoordinator,
test::{
check_signature_shares, coordinator_state_machine, equal_after_save_load,
feedback_messages, feedback_mutated_messages, new_coordinator, run_dkg_sign,
setup, setup_with_timeouts, start_dkg_round,
feedback_messages, feedback_mutated_messages, gen_nonces, new_coordinator,
run_dkg_sign, setup, setup_with_timeouts, start_dkg_round,
},
Config, Coordinator as CoordinatorTrait, State,
},
Expand Down Expand Up @@ -2812,4 +2812,14 @@ pub mod test {
assert_eq!(coordinator.current_sign_id, id);
}
}

#[test]
fn gen_nonces_v1() {
gen_nonces::<FireCoordinator<v1::Aggregator>, v1::Signer>(5, 1);
}

#[test]
fn gen_nonces_v2() {
gen_nonces::<FireCoordinator<v2::Aggregator>, v2::Signer>(5, 1);
}
}
81 changes: 81 additions & 0 deletions src/state_machine/coordinator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ pub mod fire;
#[allow(missing_docs)]
pub mod test {
use hashbrown::{HashMap, HashSet};
use rand_core::OsRng;
use std::{sync::Once, time::Duration};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};

Expand Down Expand Up @@ -972,4 +973,84 @@ pub mod test {

assert_eq!(signers, loaded_signers);
}

/// Test if a signer will generate a new nonce after a signing round as a defense
/// against a malicious coordinator who requests multiple signing rounds
/// with no nonce round in between to generate a new nonce
pub fn gen_nonces<Coordinator: CoordinatorTrait, SignerType: SignerTrait>(
num_signers: u32,
keys_per_signer: u32,
) {
let mut rng = OsRng;

let (mut coordinators, mut signers) =
run_dkg::<Coordinator, SignerType>(num_signers, keys_per_signer);

let msg = "It was many and many a year ago, in a kingdom by the sea"
.as_bytes()
.to_vec();

let signature_type = SignatureType::Frost;

// Start a signing round
let message = coordinators
.first_mut()
.unwrap()
.start_signing_round(&msg, signature_type)
.unwrap();
assert_eq!(
coordinators.first_mut().unwrap().get_state(),
State::NonceGather(signature_type)
);

// Send the NonceRequest to all signers and gather NonceResponses
// by sharing with all other signers and coordinator
let (outbound_messages, operation_results) =
feedback_messages(&mut coordinators, &mut signers, &[message]);
assert!(operation_results.is_empty());
assert_eq!(
coordinators.first_mut().unwrap().get_state(),
State::SigShareGather(signature_type)
);

// Once the coordinator has received sufficient NonceResponses,
// it should send out a SignatureShareRequest
assert_eq!(outbound_messages.len(), 1);
match &outbound_messages[0].msg {
Message::SignatureShareRequest(_) => {}
_ => {
panic!("Expected SignatureShareRequest message");
}
}

// Pass the SignatureShareRequest to the first signer and get his SignatureShares
// which should use the nonce generated before sending out NonceResponse above
let messages1 = signers[0]
.process(&outbound_messages[0].msg, &mut rng)
.unwrap();

// Pass the SignatureShareRequest to the second signer and get his SignatureShares
// which should use the nonce generated just before sending out the previous SignatureShare
let messages2 = signers[0]
.process(&outbound_messages[0].msg, &mut rng)
.unwrap();

// iterate through the responses and collect the embedded shares
// if the signer didn't generate a nonce after sending the first signature shares
// then the shares should be the same, since the message and everything else is
for (message1, message2) in messages1.into_iter().zip(messages2) {
let share1 = if let Message::SignatureShareResponse(response) = message1 {
response.signature_shares[0].clone()
} else {
panic!("Message should have been SignatureShareResponse");
};
let share2 = if let Message::SignatureShareResponse(response) = message2 {
response.signature_shares[0].clone()
} else {
panic!("Message should have been SignatureShareResponse");
};

assert_ne!(share1.z_i, share2.z_i);
}
}
}
7 changes: 5 additions & 2 deletions src/state_machine/signer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ impl<SignerType: SignerTrait> Signer<SignerType> {
self.dkg_private_shares(dkg_private_shares, rng)
}
Message::SignatureShareRequest(sign_share_request) => {
self.sign_share_request(sign_share_request)
self.sign_share_request(sign_share_request, rng)
}
Message::NonceRequest(nonce_request) => self.nonce_request(nonce_request, rng),
_ => Ok(vec![]), // TODO
Expand Down Expand Up @@ -558,9 +558,10 @@ impl<SignerType: SignerTrait> Signer<SignerType> {
Ok(msgs)
}

fn sign_share_request(
fn sign_share_request<R: RngCore + CryptoRng>(
&mut self,
sign_request: &SignatureShareRequest,
rng: &mut R,
) -> Result<Vec<Message>, Error> {
let mut msgs = vec![];

Expand Down Expand Up @@ -604,6 +605,8 @@ impl<SignerType: SignerTrait> Signer<SignerType> {
}
};

self.signer.gen_nonces(rng);

let response = SignatureShareResponse {
dkg_id: sign_request.dkg_id,
sign_id: sign_request.sign_id,
Expand Down

0 comments on commit e3ab997

Please sign in to comment.