Skip to content

Commit

Permalink
Merge pull request #31 from RolandSherwin/tests_2
Browse files Browse the repository at this point in the history
test: fuzz test to ensure that DKG completes regardless of the order in which the `Votes` are handled
  • Loading branch information
dirvine authored Oct 10, 2022
2 parents a577a03 + 844ea9c commit ef57371
Show file tree
Hide file tree
Showing 4 changed files with 656 additions and 77 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ log = "0.4.13"

[dev-dependencies]
env_logger = "0.8"
eyre = "~0.6.5"
# quickcheck = "1"
# quickcheck_macros = "1"
198 changes: 121 additions & 77 deletions src/sdkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,10 +501,11 @@ pub enum PartFault {
}

#[cfg(test)]
mod tests {
use super::{AckOutcome, PartOutcome, SyncKeyGen};
pub(crate) mod tests {
use super::{Ack, AckOutcome, Part, PartOutcome, SyncKeyGen};
use bls::{PublicKey, PublicKeySet, SecretKey, SecretKeyShare, SignatureShare};
use std::collections::BTreeMap;
use eyre::{eyre, Result};
use std::collections::{BTreeMap, BTreeSet};

#[test]
fn test_sdkg() {
Expand Down Expand Up @@ -606,121 +607,164 @@ mod tests {
assert!(pub_key_set.public_key().verify(&sig, msg));
}

fn gen_dkg_key(
threshold: usize,
node_num: usize,
) -> (Vec<(usize, SecretKeyShare)>, PublicKeySet) {
// Use the OS random number generator for any randomness:
// let mut rng = bls::rand::rngs::OsRng::fill_bytes([0u8; 16]);
let mut rng = bls::rand::rngs::OsRng;
#[test]
fn test_threshold() -> Result<()> {
for nodes_num in 2..10 {
// for threshold in 1..((nodes_num-1)/2+1) {
for threshold in 1..nodes_num {
println!("Testing for threshold {}/{}...", threshold, nodes_num);

// Generate individual key pairs for encryption. These are not suitable for threshold schemes.
let sec_keys: Vec<SecretKey> = (0..node_num).map(|_| bls::rand::random()).collect();
let (secret_key_shares, pub_key_set) = simulate_dkg_round(nodes_num, threshold)?;
let msg = "signed message";

// check threshold + 1 sigs matches master key
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold + 1] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let sig = pub_key_set
.combine_signatures(&sig_shares)
.map_err(|err| eyre!("The shares can be combined: {err:?}"))?;
assert!(pub_key_set.public_key().verify(&sig, msg));

// check threshold sigs are not enough to match master key
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let _sig = pub_key_set.combine_signatures(&sig_shares).is_err();
}
}
Ok(())
}

// Test helpers
#[allow(clippy::type_complexity)]
fn init_nodes<R: bls::rand::RngCore>(
num_nodes: usize,
threshold: usize,
rng: &mut R,
) -> Result<(BTreeMap<usize, SyncKeyGen<usize>>, Vec<(usize, Part)>)> {
let sec_keys: Vec<SecretKey> = (0..num_nodes).map(|_| bls::rand::random()).collect();
let pub_keys: BTreeMap<usize, PublicKey> = sec_keys
.iter()
.map(SecretKey::public_key)
.enumerate()
.collect();

// Create the `SyncKeyGen` instances. The constructor also outputs the part that needs to
// be sent to all other participants, so we save the parts together with their sender ID.
let mut nodes = BTreeMap::new();
let mut parts = Vec::new();
for (id, sk) in sec_keys.into_iter().enumerate() {
let (sync_key_gen, opt_part) =
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, &mut rng).unwrap_or_else(
|_| panic!("Failed to create `SyncKeyGen` instance for node #{}", id),
);
SyncKeyGen::new(id, sk, pub_keys.clone(), threshold, rng)?;
nodes.insert(id, sync_key_gen);
parts.push((id, opt_part.unwrap())); // Would be `None` for observer nodes.
}

// All nodes now handle the parts and send the resulting `Ack` messages.
Ok((nodes, parts))
}

fn handle_parts<R: bls::rand::RngCore>(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
parts: &Vec<(usize, Part)>,
rng: &mut R,
) -> Result<Vec<(usize, Ack)>> {
let mut acks = Vec::new();
for (sender_id, part) in parts {
for (&id, node) in &mut nodes {
match node
.handle_part(&sender_id, part.clone(), &mut rng)
.expect("Failed to handle Part")
{
for (&id, node) in nodes.iter_mut() {
match node.handle_part(sender_id, part.clone(), rng)? {
PartOutcome::Valid(Some(ack)) => acks.push((id, ack)),
PartOutcome::Invalid(fault) => panic!("Invalid Part: {:?}", fault),
PartOutcome::Valid(None) => {
panic!("We are not an observer, so we should send Ack.")
}
_ => return Err(eyre!("We are an observer/invalid part")),
}
}
}
Ok(acks)
}

// Finally, we handle all the `Ack`s.
fn handle_acks(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
acks: &Vec<(usize, Ack)>,
) -> Result<()> {
for (sender_id, ack) in acks {
for node in nodes.values_mut() {
match node
.handle_ack(&sender_id, ack.clone())
.expect("Failed to handle Ack")
.handle_ack(sender_id, ack.clone())
.map_err(|err| eyre!("Failed to handle Ack {err:?}"))?
{
AckOutcome::Valid => (),
AckOutcome::Invalid(fault) => panic!("Invalid Ack: {:?}", fault),
AckOutcome::Invalid(fault) => return Err(eyre!("Invalid Ack {fault:?}")),
}
}
}
Ok(())
}

fn gen_key_share(
nodes: &mut BTreeMap<usize, SyncKeyGen<usize>>,
) -> Result<(Vec<(usize, SecretKeyShare)>, PublicKeySet)> {
let mut pk_set = BTreeSet::new();

// We have all the information and can generate the key sets.
// Generate the public key set; which is identical for all nodes.
let pub_key_set = nodes[&0]
.generate()
.expect("Failed to create `PublicKeySet` from node #0")
.0;
let mut secret_key_shares = Vec::new();
for (&id, node) in &mut nodes {
assert!(node.is_ready());
let (pks, opt_sks) = node.generate().unwrap_or_else(|_| {
panic!(
"Failed to create `PublicKeySet` and `SecretKeyShare` for node #{}",
id
)
});
assert_eq!(pks, pub_key_set); // All nodes now know the public keys and public key shares.
let sks = opt_sks.expect("Not an observer node: We receive a secret key share.");
for (&id, node) in nodes {
if !node.is_ready() {
return Err(eyre!("Node: {id} is not ready"));
}
let (pks, opt_sks) = node.generate()?;
let sks = opt_sks.ok_or_else(|| eyre!("Node: {id} is an observer"))?;
pk_set.insert(pks);
secret_key_shares.push((id, sks));
}

(secret_key_shares, pub_key_set)
// verify that they produced a single pks
if pk_set.len() != 1 {
return Err(eyre!("The pub_key_set is not the same for all the nodes"));
}
let pk_set = Vec::from_iter(pk_set.into_iter());

Ok((secret_key_shares, pk_set[0].clone()))
}

#[test]
fn test_threshold() {
for nodes_num in 2..=7 {
// for threshold in 1..((nodes_num-1)/2+1) {
for threshold in 1..nodes_num {
println!("Testing for threshold {}/{}...", threshold, nodes_num);
fn simulate_dkg_round(
num_nodes: usize,
threshold: usize,
) -> Result<(Vec<(usize, SecretKeyShare)>, PublicKeySet)> {
let mut rng = bls::rand::rngs::OsRng;

let (secret_key_shares, pub_key_set) = gen_dkg_key(threshold, nodes_num);
let msg = "signed message";
let (mut nodes, parts) = init_nodes(num_nodes, threshold, &mut rng)?;
let acks = handle_parts(&mut nodes, &parts, &mut rng)?;
handle_acks(&mut nodes, &acks)?;
gen_key_share(&mut nodes)
}

// check threshold + 1 sigs matches master key
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold + 1] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let sig = pub_key_set
.combine_signatures(&sig_shares)
.expect("The shares can be combined.");
assert!(pub_key_set.public_key().verify(&sig, msg));
pub(crate) fn verify_threshold(
threshold: usize,
sk_shares: &[(usize, SecretKeyShare)],
pk_set: &PublicKeySet,
) -> Result<()> {
let msg = "verify threshold";
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();

// check threshold sigs are not enough to match master key
let mut sig_shares: BTreeMap<usize, SignatureShare> = BTreeMap::new();
for (id, sks) in &secret_key_shares[0..threshold] {
let sig_share = sks.sign(msg);
let pks = pub_key_set.public_key_share(id);
assert!(pks.verify(&sig_share, msg));
sig_shares.insert(*id, sig_share);
}
let _sig = pub_key_set.combine_signatures(&sig_shares).is_err();
for (id, sks) in sk_shares.iter().take(threshold + 1) {
let sig_share = sks.sign(msg);
let pks = pk_set.public_key_share(id);
if !pks.verify(&sig_share, msg) {
return Err(eyre!("The pub_key_share cannot verify the sig"));
}
sig_shares.insert(*id, sig_share);
}

let sig = pk_set.combine_signatures(&sig_shares)?;

if !pk_set.public_key().verify(&sig, msg) {
return Err(eyre!("The pub_key_set cannot verify the sig"));
}

Ok(())
}
}
Loading

0 comments on commit ef57371

Please sign in to comment.