From 6f33c44d2274a1c74af167a36d990217bdde4701 Mon Sep 17 00:00:00 2001 From: natalie Date: Wed, 14 Jun 2023 18:52:43 +0100 Subject: [PATCH] Return VerifiableSecretSharingCommitment (#10) * Update frost-ed25519 crate to v0.4.0 (#324) * Refactor keygen to return SecretShares and not KeyPackages (#324) * Update secret key input text Co-authored-by: Conrado Gouvea * Add commitment output (#324) * Refactor Errors in input tests (#324) * Handle errors in secret key input (#324) * Improve error handling for split_secret (#324) * Refactor validate_inputs into request_inputs function (#324) * Refactor main into cli function (#324) * Fix encoding not handling more than 9 signers (#324) * Add borrow to parameter in output to remove unecessary clones (#324) --------- Co-authored-by: Conrado Gouvea --- Cargo.lock | 185 +++++++--------- Cargo.toml | 2 +- src/cli.rs | 64 ++++++ src/inputs.rs | 6 +- src/main.rs | 71 ++---- src/output.rs | 57 ++++- src/tests/inputs_tests.rs | 50 ++--- src/tests/integration_test.rs | 62 +++++- src/tests/integration_test/signature_gen.rs | 14 +- src/tests/output_tests.rs | 229 ++++++++++++++++---- src/trusted_dealer_keygen.rs | 74 +++++-- 11 files changed, 552 insertions(+), 262 deletions(-) create mode 100644 src/cli.rs diff --git a/Cargo.lock b/Cargo.lock index 602b4cc..c509bcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,15 +9,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "autocfg" @@ -114,24 +109,29 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.25" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28" dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +dependencies = [ + "anstyle", "bitflags", "clap_lex", - "indexmap", - "textwrap", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cpufeatures" @@ -144,19 +144,19 @@ dependencies = [ [[package]] name = "criterion" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", - "atty", "cast", "ciborium", "clap", "criterion-plot", + "is-terminal", "itertools", - "lazy_static", "num-traits", + "once_cell", "oorandom", "plotters", "rayon", @@ -319,15 +319,16 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "frost-core" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2329106b2265939420de478389a83ffd63a76ec86b306c6d78ea345a5efced3" +checksum = "d2fe2b22eee8914aaf54ab74c7bc6cf71e539c40d92a746cf5c65b619acb02dc" dependencies = [ "byteorder", "criterion", "debugless-unwrap", "digest", "hex", + "itertools", "proptest", "proptest-derive", "rand_core", @@ -339,9 +340,9 @@ dependencies = [ [[package]] name = "frost-ed25519" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b749ea831d8e9205f6c8d912d5656c5d320b0bb1489b4b6fdd715941a1e8dd1a" +checksum = "24f56348765eef8f99de247aba00c1599ba980ca372aa2e4c26c4e9d11e6e4b2" dependencies = [ "curve25519-dalek", "frost-core", @@ -388,21 +389,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -427,16 +413,6 @@ dependencies = [ "serde", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - [[package]] name = "instant" version = "0.1.12" @@ -448,15 +424,27 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", "windows-sys 0.48.0", ] +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "itertools" version = "0.10.5" @@ -489,9 +477,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" [[package]] name = "libm" @@ -513,12 +501,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "memoffset" @@ -551,9 +536,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -561,12 +546,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "packed_simd_2" version = "0.3.8" @@ -628,9 +607,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -683,11 +662,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ - "proc-macro2 1.0.58", + "proc-macro2 1.0.59", ] [[package]] @@ -762,9 +741,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.2" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "regex-syntax 0.7.2", ] @@ -843,9 +822,9 @@ version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", - "syn 2.0.16", + "proc-macro2 1.0.59", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -893,19 +872,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", + "proc-macro2 1.0.59", + "quote 1.0.28", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", + "proc-macro2 1.0.59", + "quote 1.0.28", "unicode-ident", ] @@ -922,12 +901,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -943,9 +916,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", - "syn 2.0.16", + "proc-macro2 1.0.59", + "quote 1.0.28", + "syn 2.0.18", ] [[package]] @@ -972,9 +945,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-xid" @@ -994,8 +967,8 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8881d5cc0ae34e3db2f1de5af81e5117a420d2f937506c2dc20d6f4cfb069051" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", + "proc-macro2 1.0.59", + "quote 1.0.28", "syn 1.0.109", ] @@ -1043,9 +1016,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.58", - "quote 1.0.27", - "syn 2.0.16", + "proc-macro2 1.0.59", + "quote 1.0.28", + "syn 2.0.18", "wasm-bindgen-shared", ] @@ -1055,7 +1028,7 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ - "quote 1.0.27", + "quote 1.0.28", "wasm-bindgen-macro-support", ] @@ -1065,9 +1038,9 @@ version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", - "syn 2.0.16", + "proc-macro2 1.0.59", + "quote 1.0.28", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1266,7 +1239,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.58", - "quote 1.0.27", - "syn 2.0.16", + "proc-macro2 1.0.59", + "quote 1.0.28", + "syn 2.0.18", ] diff --git a/Cargo.toml b/Cargo.toml index 143ab0a..b436d12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -frost-ed25519 = "0.3.0" +frost-ed25519 = "0.4.0" thiserror = "1.0" rand = "0.8" hex = "0.4" diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..30e8c19 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,64 @@ +use frost::Error; +use frost_ed25519 as frost; +use rand::thread_rng; +use std::io; + +use crate::inputs::request_inputs; +use crate::output::{print_values, Logger}; +use crate::trusted_dealer_keygen::{split_secret, trusted_dealer_keygen}; + +#[derive(PartialEq)] +pub enum CliError { + Config, + Keygen, +} + +pub struct TrustedDealerError { + pub frost_error: Error, + pub cli_error: CliError, +} + +pub fn cli() -> Result<(), TrustedDealerError> { + let mut reader = Box::new(io::stdin().lock()); + let config = request_inputs(&mut reader); + if let Err(e) = config { + return Err(TrustedDealerError { + frost_error: e, + cli_error: CliError::Config, + }); + } + + let config = config.unwrap(); + + let mut rng = thread_rng(); + + let keygen = if config.secret.is_empty() { + trusted_dealer_keygen(&config, &mut rng) + } else { + split_secret(&config, &mut rng) + }; + + if let Err(e) = keygen { + return Err(TrustedDealerError { + frost_error: e, + cli_error: CliError::Keygen, + }); + } + + let (shares, pubkeys) = keygen.unwrap(); + + let mut console_logger = ConsoleLogger::default(); + + print_values(&shares, &pubkeys, &mut console_logger); + + Ok(()) +} + +#[derive(Default)] +pub struct ConsoleLogger; + +impl Logger for ConsoleLogger { + fn log(&mut self, value: String) { + println!("{}", value); + } +} diff --git a/src/inputs.rs b/src/inputs.rs index f5164c6..fdf0b5a 100755 --- a/src/inputs.rs +++ b/src/inputs.rs @@ -6,10 +6,10 @@ use std::io::BufRead; pub struct Config { pub min_signers: u16, pub max_signers: u16, - pub secret: Vec, // todo + pub secret: Vec, } -pub fn validate_inputs(config: &Config) -> Result<(), Error> { +fn validate_inputs(config: &Config) -> Result<(), Error> { if config.min_signers < 2 { return Err(Error::InvalidMinSigners); } @@ -57,5 +57,7 @@ pub fn request_inputs(input: &mut impl BufRead) -> Result { secret, }; + validate_inputs(&config)?; + Ok(config) } diff --git a/src/main.rs b/src/main.rs index 3e74705..8b2165d 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,62 +1,31 @@ +mod cli; mod inputs; -#[cfg(test)] -mod tests; +mod output; mod trusted_dealer_keygen; +#[cfg(test)] +mod tests; + +use cli::CliError; use std::io; -use output::{print_values, Logger}; -use rand::thread_rng; -use trusted_dealer_keygen::split_secret; -mod output; - -use crate::inputs::{request_inputs, validate_inputs}; -use crate::trusted_dealer_keygen::trusted_dealer_keygen; +use crate::cli::cli; fn main() -> io::Result<()> { - let mut reader = Box::new(io::stdin().lock()); - let config = request_inputs(&mut reader); - if let Err(e) = config { - eprintln!("Error: {}", e); - std::process::exit(exitcode::DATAERR) + let out = cli(); + + if let Err(e) = out { + if e.cli_error == CliError::Config { + { + eprintln!("Error: {}", e.frost_error); + std::process::exit(exitcode::DATAERR) + }; + }; + if e.cli_error == CliError::Keygen { + eprintln!("Error: {}", e.frost_error); + std::process::exit(1) + }; } - let config = config.unwrap(); - - let mut rng = thread_rng(); - - let valid = validate_inputs(&config); - if let Err(e) = valid { - eprintln!("Error: {}", e); - std::process::exit(exitcode::DATAERR) - } - - let keygen = if config.secret.is_empty() { - trusted_dealer_keygen(&config, &mut rng) - } else { - split_secret(&config, &mut rng) - }; - - // Print outputs - if let Err(e) = keygen { - eprintln!("Error: {}", e); - std::process::exit(1) - } - - let (key_packages, pubkeys) = keygen.unwrap(); - - let mut console_logger = ConsoleLogger::default(); - - print_values(&key_packages, pubkeys, &mut console_logger); - Ok(()) } - -#[derive(Default)] -pub struct ConsoleLogger; - -impl Logger for ConsoleLogger { - fn log(&mut self, value: String) { - println!("{}", value); - } -} diff --git a/src/output.rs b/src/output.rs index effefa5..25a91ca 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,26 +1,33 @@ -use std::collections::HashMap; - -use frost_ed25519 as frost; - -use frost::keys::{KeyPackage, PublicKeyPackage}; +use frost::keys::{PublicKeyPackage, SecretShare, VerifiableSecretSharingCommitment}; use frost::Identifier; +use frost_ed25519 as frost; use itertools::Itertools; +use std::collections::HashMap; pub trait Logger { fn log(&mut self, value: String); } +fn encode_commitment(vss_commitment: &VerifiableSecretSharingCommitment) -> String { + let serialized = vss_commitment.serialize(); + let num = serialized.len(); + + let mut out = hex::encode([num as u8]); + for cc in serialized { + out = out + &hex::encode(cc) + } + out +} + pub fn print_values( - keys: &HashMap, - pubkeys: PublicKeyPackage, + keys: &HashMap, + pubkeys: &PublicKeyPackage, logger: &mut dyn Logger, ) { logger.log(format!( "Group public key: {:x?}", hex::encode(pubkeys.group_public.to_bytes()) )); - // Need to be able to extract value for VerifiableSecretSharingCommitment that isn't currently accessible - // println!("Commitment: {:x?}", shares[0].commitment[0]); println!("---"); @@ -28,12 +35,40 @@ pub fn print_values( logger.log(format!("Participant {:?}", k)); logger.log(format!( "Secret share: {:?}", - hex::encode(v.secret_share.to_bytes()) + hex::encode(v.value.to_bytes()) )); logger.log(format!( "Public key: {:?}", - hex::encode(v.public.to_bytes()) + hex::encode(pubkeys.signer_pubkeys[k].to_bytes()) )); + logger.log(format!("Commitment: {}", encode_commitment(&v.commitment))); println!("---") } } + +#[cfg(test)] +mod tests { + use crate::output::encode_commitment; + use frost::keys::VerifiableSecretSharingCommitment; + use frost_ed25519 as frost; + use hex::FromHex; + + #[test] + fn check_encode_commitment() { + let coeff_comm_1 = "538d43e67bc9c22a3befdf24e68f29bfc9bcbd844736e5b82fdab1545bceddcf"; + let coeff_comm_2 = "6bc2053a2bedc6a071c74495965c960a6d2655720edba2a5aa68b8e160c9f55d"; + let coeff_comm_3 = "eb73cfae619afa59984754e5f3e93ba2357164ce113b09e542365d8313d6f091"; + + let expected = "03".to_string() + coeff_comm_1 + coeff_comm_2 + coeff_comm_3; + + let decoded_1 = <[u8; 32]>::from_hex(coeff_comm_1).unwrap(); + let decoded_2 = <[u8; 32]>::from_hex(coeff_comm_2).unwrap(); + let decoded_3 = <[u8; 32]>::from_hex(coeff_comm_3).unwrap(); + + let vss_commitment = + VerifiableSecretSharingCommitment::deserialize(vec![decoded_1, decoded_2, decoded_3]) + .unwrap(); + let commitment = encode_commitment(&vss_commitment); + assert!(commitment == expected) + } +} diff --git a/src/tests/inputs_tests.rs b/src/tests/inputs_tests.rs index a0ac473..b767586 100755 --- a/src/tests/inputs_tests.rs +++ b/src/tests/inputs_tests.rs @@ -1,4 +1,6 @@ -use crate::inputs::{request_inputs, validate_inputs, Config}; +use crate::inputs::{request_inputs, Config}; +use frost::Error; +use frost_ed25519 as frost; #[test] fn check_valid_input_for_signers() { @@ -8,48 +10,34 @@ fn check_valid_input_for_signers() { secret: Vec::new(), }; - let expected = validate_inputs(&config); + let mut valid_input = "2\n3\n\n".as_bytes(); + let expected = request_inputs(&mut valid_input); - assert_eq!(expected, Ok(())); + assert_eq!(expected, Ok(config)); } #[test] fn return_error_if_min_participant_greater_than_max_participant() { - let config = Config { - min_signers: 4, - max_signers: 3, - secret: Vec::new(), - }; + let mut invalid_input = "4\n3\n\n".as_bytes(); + let expected = request_inputs(&mut invalid_input); - let expected = validate_inputs(&config); - - assert_eq!(expected, Err(frost_ed25519::Error::InvalidMinSigners)); + assert_eq!(expected, Err(Error::InvalidMinSigners)); } #[test] fn return_error_if_min_participant_is_less_than_2() { - let config = Config { - min_signers: 1, - max_signers: 3, - secret: Vec::new(), - }; + let mut invalid_input = "1\n3\n\n".as_bytes(); + let expected = request_inputs(&mut invalid_input); - let expected = validate_inputs(&config); - - assert_eq!(expected, Err(frost_ed25519::Error::InvalidMinSigners)); + assert_eq!(expected, Err(Error::InvalidMinSigners)); } #[test] fn return_error_if_max_participant_is_less_than_2() { - let config = Config { - min_signers: 2, - max_signers: 1, - secret: Vec::new(), - }; + let mut invalid_input = "2\n1\n\n".as_bytes(); + let expected = request_inputs(&mut invalid_input); - let expected = validate_inputs(&config); - - assert_eq!(expected, Err(frost_ed25519::Error::InvalidMaxSigners)); + assert_eq!(expected, Err(Error::InvalidMaxSigners)); } // Testing inclusion of secret input @@ -78,7 +66,7 @@ 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, Err(frost_ed25519::Error::InvalidMinSigners)) + assert_eq!(expected, Err(Error::InvalidMinSigners)) } #[test] @@ -86,13 +74,13 @@ 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(frost_ed25519::Error::InvalidMaxSigners)) + assert_eq!(expected, Err(Error::InvalidMaxSigners)) } #[test] -fn return_error_if_secret_is_invalid() { +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(frost_ed25519::Error::MalformedSigningKey)) + assert_eq!(expected, Err(Error::MalformedSigningKey)) } diff --git a/src/tests/integration_test.rs b/src/tests/integration_test.rs index 9b5e756..11eaf70 100644 --- a/src/tests/integration_test.rs +++ b/src/tests/integration_test.rs @@ -1,4 +1,5 @@ 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; @@ -14,7 +15,32 @@ fn check_keygen_with_dealer() { max_signers: 3, secret: Vec::new(), }; - let (key_packages, pubkeys) = trusted_dealer_keygen(&config, &mut rng).unwrap(); + let (shares, pubkeys) = trusted_dealer_keygen(&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); + + assert!(verify_signature.is_ok()); +} + +#[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) = trusted_dealer_keygen(&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(); @@ -41,7 +67,39 @@ fn check_keygen_with_dealer_with_secret() { max_signers: 3, secret, }; - let (key_packages, pubkeys) = split_secret(&secret_config, &mut rng).unwrap(); + 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!(verify_signature.is_ok()); +} + +#[test] +fn check_keygen_with_dealer_with_secret_with_large_num_of_signers() { + let mut rng = thread_rng(); + let secret: Vec = 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, diff --git a/src/tests/integration_test/signature_gen.rs b/src/tests/integration_test/signature_gen.rs index a48fa28..d8f92a9 100644 --- a/src/tests/integration_test/signature_gen.rs +++ b/src/tests/integration_test/signature_gen.rs @@ -1,4 +1,4 @@ -use frost::keys::KeyPackage; +use frost::keys::{KeyPackage, SecretShare}; use frost::round1::{SigningCommitments, SigningNonces}; use frost::round2::SignatureShare; use frost::{Identifier, SigningPackage}; @@ -6,6 +6,18 @@ use frost_ed25519 as frost; use rand::rngs::ThreadRng; use std::collections::HashMap; +pub fn generate_key_packages( + shares: HashMap, +) -> HashMap { + 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, diff --git a/src/tests/output_tests.rs b/src/tests/output_tests.rs index 73ceff7..c74e707 100644 --- a/src/tests/output_tests.rs +++ b/src/tests/output_tests.rs @@ -14,6 +14,15 @@ impl Logger for TestLogger { } } +fn encode_commitment_helper(commitment: Vec<[u8; 32]>) -> String { + let len_test = commitment.len() as u8; + let mut out = hex::encode([len_test]); + for c in commitment { + out = out + &hex::encode(c) + } + out +} + #[test] fn check_output_without_secret() { let mut test_logger = TestLogger(Vec::new()); @@ -23,9 +32,9 @@ fn check_output_without_secret() { max_signers: 3, secret: Vec::new(), }; - let (key_packages, pubkeys) = trusted_dealer_keygen(&config, &mut rng).unwrap(); + let (shares, pubkeys) = trusted_dealer_keygen(&config, &mut rng).unwrap(); - print_values(&key_packages, pubkeys, &mut test_logger); + print_values(&shares, &pubkeys, &mut test_logger); let signer_1 = Identifier::try_from(1).unwrap(); let signer_2 = Identifier::try_from(2).unwrap(); @@ -35,7 +44,7 @@ fn check_output_without_secret() { test_logger.0[0], format!( "Group public key: \"{}\"", - hex::encode(key_packages[&signer_1].group_public.to_bytes()) + hex::encode(pubkeys.group_public.to_bytes()) ) ); @@ -44,46 +53,67 @@ fn check_output_without_secret() { test_logger.0[2], format!( "Secret share: \"{}\"", - hex::encode(key_packages[&signer_1].secret_share.to_bytes()) + hex::encode(shares[&signer_1].value.to_bytes()) ) ); assert_eq!( test_logger.0[3], format!( "Public key: \"{}\"", - hex::encode(key_packages[&signer_1].public.to_bytes()) + hex::encode(pubkeys.signer_pubkeys[&signer_1].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[4], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_1].commitment.serialize()) ) ); - assert_eq!(test_logger.0[4], format!("Participant {:?}", signer_2)); - assert_eq!( - test_logger.0[5], - format!( - "Secret share: \"{}\"", - hex::encode(key_packages[&signer_2].secret_share.to_bytes()) - ) - ); + assert_eq!(test_logger.0[5], format!("Participant {:?}", signer_2)); assert_eq!( test_logger.0[6], format!( - "Public key: \"{}\"", - hex::encode(key_packages[&signer_2].public.to_bytes()) + "Secret share: \"{}\"", + hex::encode(shares[&signer_2].value.to_bytes()) + ) + ); + assert_eq!( + test_logger.0[7], + format!( + "Public key: \"{}\"", + hex::encode(pubkeys.signer_pubkeys[&signer_2].to_bytes()) ) ); - - assert_eq!(test_logger.0[7], format!("Participant {:?}", signer_3)); assert_eq!( test_logger.0[8], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_2].commitment.serialize()) + ) + ); + + assert_eq!(test_logger.0[9], format!("Participant {:?}", signer_3)); + assert_eq!( + test_logger.0[10], format!( "Secret share: \"{}\"", - hex::encode(key_packages[&signer_3].secret_share.to_bytes()) + hex::encode(shares[&signer_3].value.to_bytes()) ) ); assert_eq!( - test_logger.0[9], + test_logger.0[11], format!( "Public key: \"{}\"", - hex::encode(key_packages[&signer_3].public.to_bytes()) + hex::encode(pubkeys.signer_pubkeys[&signer_3].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[12], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_3].commitment.serialize()) ) ); } @@ -101,9 +131,9 @@ fn check_output_with_secret() { max_signers: 3, secret, }; - let (key_packages, pubkeys) = split_secret(&config, &mut rng).unwrap(); + let (shares, pubkeys) = split_secret(&config, &mut rng).unwrap(); - print_values(&key_packages, pubkeys, &mut test_logger); + print_values(&shares, &pubkeys, &mut test_logger); let signer_1 = Identifier::try_from(1).unwrap(); let signer_2 = Identifier::try_from(2).unwrap(); @@ -113,7 +143,7 @@ fn check_output_with_secret() { test_logger.0[0], format!( "Group public key: \"{}\"", - hex::encode(key_packages[&signer_1].group_public.to_bytes()) + hex::encode(pubkeys.group_public.to_bytes()) ) ); @@ -122,46 +152,165 @@ fn check_output_with_secret() { test_logger.0[2], format!( "Secret share: \"{}\"", - hex::encode(key_packages[&signer_1].secret_share.to_bytes()) + hex::encode(shares[&signer_1].value.to_bytes()) ) ); assert_eq!( test_logger.0[3], format!( "Public key: \"{}\"", - hex::encode(key_packages[&signer_1].public.to_bytes()) + hex::encode(pubkeys.signer_pubkeys[&signer_1].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[4], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_1].commitment.serialize()) ) ); - assert_eq!(test_logger.0[4], format!("Participant {:?}", signer_2)); - assert_eq!( - test_logger.0[5], - format!( - "Secret share: \"{}\"", - hex::encode(key_packages[&signer_2].secret_share.to_bytes()) - ) - ); + assert_eq!(test_logger.0[5], format!("Participant {:?}", signer_2)); assert_eq!( test_logger.0[6], format!( - "Public key: \"{}\"", - hex::encode(key_packages[&signer_2].public.to_bytes()) + "Secret share: \"{}\"", + hex::encode(shares[&signer_2].value.to_bytes()) + ) + ); + assert_eq!( + test_logger.0[7], + format!( + "Public key: \"{}\"", + hex::encode(pubkeys.signer_pubkeys[&signer_2].to_bytes()) ) ); - - assert_eq!(test_logger.0[7], format!("Participant {:?}", signer_3)); assert_eq!( test_logger.0[8], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_2].commitment.serialize()) + ) + ); + + assert_eq!(test_logger.0[9], format!("Participant {:?}", signer_3)); + assert_eq!( + test_logger.0[10], format!( "Secret share: \"{}\"", - hex::encode(key_packages[&signer_3].secret_share.to_bytes()) + hex::encode(shares[&signer_3].value.to_bytes()) ) ); assert_eq!( - test_logger.0[9], + test_logger.0[11], format!( "Public key: \"{}\"", - hex::encode(key_packages[&signer_3].public.to_bytes()) + hex::encode(pubkeys.signer_pubkeys[&signer_3].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[12], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_3].commitment.serialize()) + ) + ); +} + +#[test] +fn check_output_with_large_num_of_signers() { + let mut test_logger = TestLogger(Vec::new()); + let mut rng = thread_rng(); + let config = Config { + min_signers: 10, + max_signers: 20, + secret: Vec::new(), + }; + let (shares, pubkeys) = trusted_dealer_keygen(&config, &mut rng).unwrap(); + + print_values(&shares, &pubkeys, &mut test_logger); + + let signer_10 = Identifier::try_from(10).unwrap(); + + assert_eq!( + test_logger.0[0], + format!( + "Group public key: \"{}\"", + hex::encode(pubkeys.group_public.to_bytes()) + ) + ); + + assert_eq!(test_logger.0[37], format!("Participant {:?}", signer_10)); + assert_eq!( + test_logger.0[38], + format!( + "Secret share: \"{}\"", + hex::encode(shares[&signer_10].value.to_bytes()) + ) + ); + assert_eq!( + test_logger.0[39], + format!( + "Public key: \"{}\"", + hex::encode(pubkeys.signer_pubkeys[&signer_10].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[40], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_10].commitment.serialize()) + ) + ); +} + +#[test] +fn check_output_with_secret_with_large_num_of_signers() { + let mut test_logger = TestLogger(Vec::new()); + let mut rng = thread_rng(); + let secret: Vec = 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 config = Config { + min_signers: 10, + max_signers: 20, + secret, + }; + let (shares, pubkeys) = split_secret(&config, &mut rng).unwrap(); + + print_values(&shares, &pubkeys, &mut test_logger); + + let signer_10 = Identifier::try_from(10).unwrap(); + + assert_eq!( + test_logger.0[0], + format!( + "Group public key: \"{}\"", + hex::encode(pubkeys.group_public.to_bytes()) + ) + ); + + assert_eq!(test_logger.0[37], format!("Participant {:?}", signer_10)); + assert_eq!( + test_logger.0[38], + format!( + "Secret share: \"{}\"", + hex::encode(shares[&signer_10].value.to_bytes()) + ) + ); + assert_eq!( + test_logger.0[39], + format!( + "Public key: \"{}\"", + hex::encode(pubkeys.signer_pubkeys[&signer_10].to_bytes()) + ) + ); + assert_eq!( + test_logger.0[40], + format!( + "Commitment: {}", + encode_commitment_helper(shares[&signer_10].commitment.serialize()) ) ); } diff --git a/src/trusted_dealer_keygen.rs b/src/trusted_dealer_keygen.rs index 9fee1ec..4e73918 100644 --- a/src/trusted_dealer_keygen.rs +++ b/src/trusted_dealer_keygen.rs @@ -1,4 +1,4 @@ -use frost::keys::{KeyPackage, PublicKeyPackage}; +use frost::keys::{PublicKeyPackage, SecretShare}; use frost::{Error, Identifier, SigningKey}; use frost_ed25519 as frost; use rand::rngs::ThreadRng; @@ -9,34 +9,74 @@ use crate::inputs::Config; pub fn trusted_dealer_keygen( config: &Config, rng: &mut ThreadRng, -) -> Result<(HashMap, PublicKeyPackage), Error> { +) -> Result<(HashMap, PublicKeyPackage), Error> { let (shares, pubkeys) = frost::keys::generate_with_dealer(config.max_signers, config.min_signers, rng)?; - let mut key_packages: HashMap<_, _> = HashMap::new(); - - for (k, v) in shares { - let key_package = frost::keys::KeyPackage::try_from(v)?; - key_packages.insert(k, key_package); + for (_k, v) in shares.clone() { + frost::keys::KeyPackage::try_from(v)?; } - Ok((key_packages, pubkeys)) + Ok((shares, pubkeys)) } pub fn split_secret( config: &Config, rng: &mut ThreadRng, -) -> Result<(HashMap, PublicKeyPackage), Error> { - let sec = config.secret.clone(); - let again = sec.try_into().unwrap(); - let secret_key = SigningKey::from_bytes(again)?; +) -> Result<(HashMap, PublicKeyPackage), Error> { + let secret_key = SigningKey::from_bytes( + config + .secret + .clone() + .try_into() + .map_err(|_| Error::MalformedSigningKey)?, + )?; let (shares, pubkeys) = frost::keys::split(&secret_key, config.max_signers, config.min_signers, rng)?; - let mut key_packages: HashMap<_, _> = HashMap::new(); - for (k, v) in shares { - let key_package = frost::keys::KeyPackage::try_from(v)?; - key_packages.insert(k, key_package); + for (_k, v) in shares.clone() { + frost::keys::KeyPackage::try_from(v)?; + } + + Ok((shares, pubkeys)) +} + +#[cfg(test)] +mod tests { + + use rand::thread_rng; + + use crate::{inputs::Config, trusted_dealer_keygen::split_secret}; + + #[test] + fn return_malformed_signing_key_error_if_secret_is_invalid() { + let mut rng = thread_rng(); + let secret_config = Config { + min_signers: 2, + max_signers: 3, + secret: b"helloIamaninvalidsecret111111111".to_vec(), + }; + + let out = split_secret(&secret_config, &mut rng); + + assert!(out.is_err()); + } + + #[test] + fn return_malformed_signing_key_error_if_secret_is_invalid_type() { + let mut rng = thread_rng(); + let secret: Vec = 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, + ]; + let secret_config = Config { + min_signers: 2, + max_signers: 3, + secret, + }; + + let out = split_secret(&secret_config, &mut rng); + + assert!(out.is_err()); } - Ok((key_packages, pubkeys)) }