add DKG tests

This commit is contained in:
Conrado Gouvea 2023-07-13 20:38:35 -03:00
parent d0cb462907
commit 12cffcce24
6 changed files with 319 additions and 244 deletions

10
Cargo.lock generated
View File

@ -316,6 +316,7 @@ dependencies = [
"frost-ed25519",
"hex",
"itertools 0.11.0",
"pipe",
"rand",
"serde_json",
"thiserror",
@ -624,6 +625,15 @@ dependencies = [
"rand",
]
[[package]]
name = "pipe"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c7b8f27da217eb966df4c58d4159ea939431950ca03cf782c22bd7c5c1d8d75"
dependencies = [
"crossbeam-channel",
]
[[package]]
name = "platforms"
version = "3.0.2"

View File

@ -14,3 +14,4 @@ rand = "0.8"
serde_json = "1.0"
itertools = "0.11.0"
exitcode = "1.1.2"
pipe = "0.4.0"

View File

@ -71,7 +71,7 @@ pub fn request_inputs(
let u16_identifier = identifier_input
.trim()
.parse::<u16>()
.map_err(|_| eyre!("Invalid identifier"))?;
.map_err(|_| Error::MalformedIdentifier)?;
let identifier = u16_identifier.try_into()?;
let config = Config {

View File

@ -1,86 +1,93 @@
// use crate::inputs::{request_inputs, Config};
// use frost::Error;
// use frost_ed25519 as frost;
use std::io::BufWriter;
// #[test]
// fn check_valid_input_for_signers() {
// let config = Config {
// min_signers: 2,
// max_signers: 3,
// secret: Vec::new(),
// };
use crate::inputs::{request_inputs, Config};
use frost::Error;
use frost_ed25519 as frost;
// let mut valid_input = "2\n3\n\n".as_bytes();
// let expected = request_inputs(&mut valid_input);
#[test]
fn check_valid_input_for_signers() {
let config = Config {
min_signers: 2,
max_signers: 3,
identifier: 1u16.try_into().unwrap(),
};
// assert_eq!(expected, Ok(config));
// }
let mut buf = BufWriter::new(Vec::new());
let mut valid_input = "2\n3\n1\n".as_bytes();
let expected = request_inputs(&mut valid_input, &mut buf).unwrap();
// #[test]
// fn return_error_if_min_participant_greater_than_max_participant() {
// let mut invalid_input = "4\n3\n\n".as_bytes();
// let expected = request_inputs(&mut invalid_input);
assert_eq!(expected, config);
}
// assert_eq!(expected, Err(Error::InvalidMinSigners));
// }
#[test]
fn return_error_if_min_participant_greater_than_max_participant() {
let mut invalid_input = "4\n3\n1\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// #[test]
// fn return_error_if_min_participant_is_less_than_2() {
// let mut invalid_input = "1\n3\n\n".as_bytes();
// let expected = request_inputs(&mut invalid_input);
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::InvalidMinSigners
);
}
// assert_eq!(expected, Err(Error::InvalidMinSigners));
// }
#[test]
fn return_error_if_min_participant_is_less_than_2() {
let mut invalid_input = "1\n3\n1\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// #[test]
// fn return_error_if_max_participant_is_less_than_2() {
// let mut invalid_input = "2\n1\n\n".as_bytes();
// let expected = request_inputs(&mut invalid_input);
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::InvalidMinSigners
);
}
// assert_eq!(expected, Err(Error::InvalidMaxSigners));
// }
#[test]
fn return_error_if_max_participant_is_less_than_2() {
let mut invalid_input = "2\n1\n1\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// // Testing inclusion of secret input
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::InvalidMaxSigners
);
}
// #[test]
// fn check_valid_input_with_secret() {
// let mut valid_input =
// "3\n6\n7b1c33d3f5291d85de664833beb1ad469f7fb6025a0ec78b3a790c6e13a98304\n".as_bytes();
// let config = request_inputs(&mut valid_input).unwrap();
#[test]
fn return_error_if_invalid_min_signers_input() {
let mut invalid_input = "hello\n6\n1\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// let secret: Vec<u8> = vec![
// 123, 28, 51, 211, 245, 41, 29, 133, 222, 102, 72, 51, 190, 177, 173, 70, 159, 127, 182, 2,
// 90, 14, 199, 139, 58, 121, 12, 110, 19, 169, 131, 4,
// ];
// let expected = Config {
// min_signers: 3,
// max_signers: 6,
// secret,
// };
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::InvalidMinSigners
);
}
// assert_eq!(expected, config)
// }
#[test]
fn return_error_if_invalid_max_signers_input() {
let mut invalid_input = "4\nworld\n1\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// #[test]
// fn return_error_if_invalid_min_signers_input() {
// let mut invalid_input = "hello\n6\n\n".as_bytes();
// let expected = request_inputs(&mut invalid_input);
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::InvalidMaxSigners
);
}
// assert_eq!(expected, Err(Error::InvalidMinSigners))
// }
#[test]
fn return_malformed_identifier_error_if_identifier_invalid() {
let mut invalid_input = "4\n6\nasecret\n".as_bytes();
let mut buf = BufWriter::new(Vec::new());
let expected = request_inputs(&mut invalid_input, &mut buf).unwrap_err();
// #[test]
// fn return_error_if_invalid_max_signers_input() {
// let mut invalid_input = "4\nworld\n\n".as_bytes();
// let expected = request_inputs(&mut invalid_input);
// assert_eq!(expected, Err(Error::InvalidMaxSigners))
// }
// #[test]
// fn return_malformed_signing_key_error_if_secret_is_invalid() {
// let mut secret_input = "4\n6\nasecret\n".as_bytes();
// let expected = request_inputs(&mut secret_input);
// assert_eq!(expected, Err(Error::MalformedSigningKey))
// }
println!("{:?}", expected);
assert_eq!(
*expected.downcast::<Error>().unwrap(),
Error::MalformedIdentifier
);
}

View File

@ -1,118 +1,238 @@
// use crate::inputs::Config;
// use crate::tests::integration_test::signature_gen::generate_key_packages;
// use crate::trusted_dealer_keygen::split_secret;
// use frost_ed25519 as frost;
// use rand::thread_rng;
use std::collections::HashMap;
use std::io::{BufRead, Write};
use std::thread;
// use crate::trusted_dealer_keygen::dkg;
// mod signature_gen;
use frost_ed25519::keys::{KeyPackage, PublicKeyPackage};
use frost_ed25519::Identifier;
// #[test]
// fn check_keygen_with_dealer() {
// let mut rng = thread_rng();
// let config = Config {
// min_signers: 2,
// max_signers: 3,
// secret: Vec::new(),
// };
// let (shares, pubkeys) = dkg(&config, &mut rng).unwrap();
// let key_packages = generate_key_packages(shares);
// let (nonces, commitments) =
// signature_gen::generate_nonces_and_commitments(config.min_signers, &key_packages, &mut rng);
// let message = "message to sign".as_bytes();
// let comms = commitments.into_values().collect();
// let signing_package = frost::SigningPackage::new(comms, message.to_vec());
// let signature_shares =
// signature_gen::generate_signature_shares(nonces, &key_packages, &signing_package);
// let group_signature =
// frost::aggregate(&signing_package, &signature_shares[..], &pubkeys).unwrap();
// let verify_signature = pubkeys.group_public.verify(message, &group_signature);
use crate::cli::cli;
// assert!(verify_signature.is_ok());
// }
// Read a single line from the given reader.
fn read_line(mut reader: impl BufRead) -> Result<String, std::io::Error> {
let mut s = String::new();
reader.read_line(&mut s).map(|_| s)
}
// #[test]
// fn check_keygen_with_dealer_with_large_num_of_signers() {
// let mut rng = thread_rng();
// let config = Config {
// min_signers: 14,
// max_signers: 20,
// secret: Vec::new(),
// };
// let (shares, pubkeys) = dkg(&config, &mut rng).unwrap();
// let key_packages = generate_key_packages(shares);
// let (nonces, commitments) =
// signature_gen::generate_nonces_and_commitments(config.min_signers, &key_packages, &mut rng);
// let message = "message to sign".as_bytes();
// let comms = commitments.into_values().collect();
// let signing_package = frost::SigningPackage::new(comms, message.to_vec());
// let signature_shares =
// signature_gen::generate_signature_shares(nonces, &key_packages, &signing_package);
// let group_signature =
// frost::aggregate(&signing_package, &signature_shares[..], &pubkeys).unwrap();
// let verify_signature = pubkeys.group_public.verify(message, &group_signature);
// Test if the DKG CLI works.
//
// This simulates 3 simultaneous CLIs by using threads.
//
// Since the `pipe` module used for sending and receiving to each thread
// is synchronous, the test is very strict. For example, you won't be able to
// read from a CLI if it's waiting for input, and you can't write to it if it's
// waiting for some output to be read.
//
// If the test gets stuck somewhere, that's likely the reason: you should be
// writing to a CLI instead of reading, or vice-versa. Use `debug` to find
// where in the function it's getting stuck and check if the test at that point
// is correct.
#[test]
#[allow(clippy::needless_range_loop)]
fn check_dkg() {
let mut input_writers = Vec::new();
let mut output_readers = Vec::new();
let mut join_handles = Vec::new();
// assert!(verify_signature.is_ok());
// }
for i in 0..3 {
// Spawn CLIs, one thread per participant
// #[test]
// fn check_keygen_with_dealer_with_secret() {
// let mut rng = thread_rng();
// let secret: Vec<u8> = vec![
// 123, 28, 51, 211, 245, 41, 29, 133, 222, 102, 72, 51, 190, 177, 173, 70, 159, 127, 182, 2,
// 90, 14, 199, 139, 58, 121, 12, 110, 19, 169, 131, 4,
// ];
// let secret_config = Config {
// min_signers: 2,
// max_signers: 3,
// secret,
// };
// let (shares, pubkeys) = split_secret(&secret_config, &mut rng).unwrap();
// let key_packages = generate_key_packages(shares);
// let (nonces, commitments) = signature_gen::generate_nonces_and_commitments(
// secret_config.min_signers,
// &key_packages,
// &mut rng,
// );
// let message = "message to sign".as_bytes();
// let comms = commitments.into_values().collect();
// let signing_package = frost::SigningPackage::new(comms, message.to_vec());
// let signature_shares =
// signature_gen::generate_signature_shares(nonces, &key_packages, &signing_package);
// let group_signature =
// frost::aggregate(&signing_package, &signature_shares[..], &pubkeys).unwrap();
// let verify_signature = pubkeys.group_public.verify(message, &group_signature);
let (mut input_reader, input_writer) = pipe::pipe();
let (output_reader, mut output_writer) = pipe::pipe();
join_handles.push(thread::spawn(move || {
cli(&mut input_reader, &mut output_writer).unwrap()
}));
input_writers.push(input_writer);
output_readers.push(output_reader);
// assert!(verify_signature.is_ok());
// }
// Input the config into each CLI
// #[test]
// fn check_keygen_with_dealer_with_secret_with_large_num_of_signers() {
// let mut rng = thread_rng();
// let secret: Vec<u8> = vec![
// 123, 28, 51, 211, 245, 41, 29, 133, 222, 102, 72, 51, 190, 177, 173, 70, 159, 127, 182, 2,
// 90, 14, 199, 139, 58, 121, 12, 110, 19, 169, 131, 4,
// ];
// let secret_config = Config {
// min_signers: 14,
// max_signers: 20,
// secret,
// };
// let (shares, pubkeys) = split_secret(&secret_config, &mut rng).unwrap();
// let key_packages = generate_key_packages(shares);
// let (nonces, commitments) = signature_gen::generate_nonces_and_commitments(
// secret_config.min_signers,
// &key_packages,
// &mut rng,
// );
// let message = "message to sign".as_bytes();
// let comms = commitments.into_values().collect();
// let signing_package = frost::SigningPackage::new(comms, message.to_vec());
// let signature_shares =
// signature_gen::generate_signature_shares(nonces, &key_packages, &signing_package);
// let group_signature =
// frost::aggregate(&signing_package, &signature_shares[..], &pubkeys).unwrap();
// let verify_signature = pubkeys.group_public.verify(message, &group_signature);
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"The minimum number of signers: (2 or more)\n"
);
writeln!(&mut input_writers[i], "2").unwrap();
// assert!(verify_signature.is_ok());
// }
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"The maximum number of signers:\n"
);
writeln!(&mut input_writers[i], "3").unwrap();
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Your identifier (this should be an integer between 1 and 65535):\n"
);
writeln!(&mut input_writers[i], "{}", i + 1).unwrap();
}
let mut round1_packages = HashMap::new();
for i in 0..3 {
// Read the Round 1 Packages printed by each participant;
// put them in a map
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"=== ROUND 1: SEND PACKAGES ===\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
assert!(read_line(&mut output_readers[i])
.unwrap()
.starts_with("Round 1 Package to send to all other participants"));
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
let round1_package_json = read_line(&mut output_readers[i]).unwrap();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
round1_packages.insert(i, round1_package_json);
}
let mut round2_packages = HashMap::new();
for i in 0..3 {
// Input the Round 1 Packages from other participants, for each
// participant i
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"=== ROUND 1: RECEIVE PACKAGES ===\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Input Round 1 Packages from the other 2 participants.\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
for j in 0..3 {
// Input Round 1 Package from participant j
if i == j {
continue;
};
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"The sender's identifier (hex string):\n"
);
// Write j's identifier
let jid: Identifier = ((j + 1) as u16).try_into().unwrap();
writeln!(&mut input_writers[i], "{}", hex::encode(jid.serialize())).unwrap();
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Their JSON-encoded Round 1 Package:\n"
);
// Write j's package
write!(&mut input_writers[i], "{}", round1_packages[&j]).unwrap();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
}
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"=== ROUND 2: SEND PACKAGES ===\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
let mut packages = HashMap::new();
for j in 0..3 {
// Read Round 2 packages to send to other participants, for
// each participant
if i == j {
continue;
};
// Read line indicating who should receive that package;
// extract hex identifier
let s = read_line(&mut output_readers[i]).unwrap();
assert!(s.starts_with("Round 2 Package to send to participant"));
let participant_hex = s.split('\"').collect::<Vec<_>>()[1].to_string();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
// Read Round 2 package
let round2_package_json = read_line(&mut output_readers[i]).unwrap();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
packages.insert(participant_hex, round2_package_json);
}
round2_packages.insert(i, packages);
}
let mut public_key_packages = HashMap::new();
for i in 0..3 {
// Input Round 2 packages from other participants, for each participant
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"=== ROUND 2: RECEIVE PACKAGES ===\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Input Round 2 Packages from the other 2 participants.\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
for j in 0..3 {
// Input Round 2 Package from participant j
if i == j {
continue;
};
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"The sender's identifier (hex string):\n"
);
// Write j's identifier
let jid: Identifier = ((j + 1) as u16).try_into().unwrap();
writeln!(&mut input_writers[i], "{}", hex::encode(jid.serialize())).unwrap();
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Their JSON-encoded Round 2 Package:\n"
);
// Write j's package sent to i
let iid: Identifier = ((i + 1) as u16).try_into().unwrap();
let iids = hex::encode(iid.serialize());
let s = round2_packages.get(&j).expect("j").get(&iids).expect("i");
write!(&mut input_writers[i], "{}", s).unwrap();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
}
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"=== DKG FINISHED ===\n"
);
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Participant key package:\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
// Read key package
let key_package_json = read_line(&mut output_readers[i]).unwrap();
let _key_package: KeyPackage = serde_json::from_str(&key_package_json).unwrap();
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
assert_eq!(
read_line(&mut output_readers[i]).unwrap(),
"Participant public key package:\n"
);
assert_eq!(read_line(&mut output_readers[i]).unwrap(), "\n");
// Read public key package
let public_key_package_json = read_line(&mut output_readers[i]).unwrap();
let public_key_package: PublicKeyPackage =
serde_json::from_str(&public_key_package_json).unwrap();
public_key_packages.insert(i, public_key_package);
}
// Check that all public key packages are equal
assert!(public_key_packages
.values()
.all(|p| *p == public_key_packages[&0]));
// Wait for threads, which should terminate at this point
for jh in join_handles {
jh.join().unwrap();
}
}

View File

@ -1,63 +0,0 @@
use frost::keys::{KeyPackage, SecretShare};
use frost::round1::{SigningCommitments, SigningNonces};
use frost::round2::SignatureShare;
use frost::{Identifier, SigningPackage};
use frost_ed25519 as frost;
use rand::rngs::ThreadRng;
use std::collections::HashMap;
pub fn generate_key_packages(
shares: HashMap<Identifier, SecretShare>,
) -> HashMap<Identifier, KeyPackage> {
let mut key_packages: HashMap<_, _> = HashMap::new();
for (k, v) in shares {
let key_package = frost::keys::KeyPackage::try_from(v).unwrap();
key_packages.insert(k, key_package);
}
key_packages
}
pub fn generate_nonces_and_commitments(
min_signers: u16,
key_packages: &HashMap<Identifier, KeyPackage>,
rng: &mut ThreadRng,
) -> (
HashMap<Identifier, SigningNonces>,
HashMap<Identifier, SigningCommitments>,
) {
let mut nonces = HashMap::new();
let mut commitments = HashMap::new();
for participant_index in 1..(min_signers + 1) {
let participant_identifier = participant_index.try_into().expect("should be nonzero");
let (nonce, commitment) = frost::round1::commit(
participant_identifier,
key_packages[&participant_identifier].secret_share(),
rng,
);
nonces.insert(participant_identifier, nonce);
commitments.insert(participant_identifier, commitment);
}
(nonces, commitments)
}
pub fn generate_signature_shares(
nonces: HashMap<Identifier, SigningNonces>,
key_packages: &HashMap<Identifier, KeyPackage>,
signing_package: &SigningPackage,
) -> Vec<SignatureShare> {
let mut signature_shares = Vec::new();
for participant_identifier in nonces.keys() {
let key_package = &key_packages[participant_identifier];
let nonces_to_use = &nonces[participant_identifier];
let signature_share =
frost::round2::sign(signing_package, nonces_to_use, key_package).unwrap(); //TODO: handle errors
signature_shares.push(signature_share);
}
signature_shares
}