trusted-dealer: allow reading params from arguments, and writing to files (#132)

This commit is contained in:
Conrado Gouvea 2024-01-31 11:01:09 -03:00 committed by GitHub
parent 4925183519
commit d0c8949c32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 129 additions and 42 deletions

1
Cargo.lock generated
View File

@ -1292,6 +1292,7 @@ dependencies = [
name = "trusted-dealer"
version = "0.1.0"
dependencies = [
"clap",
"exitcode",
"frost-ed25519",
"hex",

View File

@ -41,7 +41,15 @@ async fn trusted_dealer_journey() {
let dealer_input = "3\n5\n\n";
let dealer_config = trusted_dealer_input(&mut dealer_input.as_bytes(), &mut buf).unwrap();
let dealer_config = trusted_dealer_input(
&trusted_dealer::args::Args {
cli: true,
..Default::default()
},
&mut dealer_input.as_bytes(),
&mut buf,
)
.unwrap();
let (shares, pubkeys) =
trusted_dealer_keygen(&dealer_config, IdentifierList::Default, &mut rng).unwrap();

View File

@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
frost-ed25519 = { version = "1.0.0-rc.0", features = ["serde"] }
reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "81c649c412e5b6ba56d491d2857f91fbd28adbc7", features = ["frost"] }
clap = { version = "4.4.7", features = ["derive"] }
thiserror = "1.0"
rand = "0.8"
hex = "0.4"

View File

@ -0,0 +1,35 @@
use clap::Parser;
#[derive(Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// CLI mode. If enabled, it will prompts for inputs from stdin
/// and print values to stdout, ignoring other flags.
#[arg(short = 'i', long, default_value_t = false)]
pub cli: bool,
/// Where to write the public key package to use. Can be a file path or "-".
/// If "-" is specified, then it will be written to standard output.
#[arg(short = 'P', long, default_value = "public-key-package.json")]
pub public_key_package: String,
/// Template for the key package to be written. If "-" is specified, they will
/// be all written to standard output. Otherwise, they will be written
/// to files using the specified format, replacing "{}" with the index
/// of the participant starting from 1.
#[arg(short = 'k', long, default_value = "key-package-{}.json")]
pub key_package: String,
/// The threshold (minimum number of signers).
#[arg(short = 't', long, default_value_t = 2)]
pub threshold: u16,
/// The total number of participants (maximum number of signers).
#[arg(short = 'n', long, default_value_t = 3)]
pub num_signers: u16,
/// The key to use when splitting into shares, in hex format. If not
/// specified, a random one will be generated.
#[arg(long)]
pub key: Option<String>,
}

View File

@ -7,15 +7,17 @@ use frost::keys::IdentifierList;
use rand::thread_rng;
use std::io::{BufRead, Write};
use crate::args::Args;
use crate::inputs::{print_values, request_inputs};
use crate::trusted_dealer_keygen::{split_secret, trusted_dealer_keygen};
// Currently this uses the Default Identifiers
pub fn cli(
args: &Args,
input: &mut impl BufRead,
logger: &mut impl Write,
) -> Result<(), Box<dyn std::error::Error>> {
let config = request_inputs(input, logger)?;
let config = request_inputs(args, input, logger)?;
let mut rng = thread_rng();
@ -27,7 +29,7 @@ pub fn cli(
let (shares, pubkeys) = keygen?;
print_values(&shares, &pubkeys, logger)?;
print_values(args, &shares, &pubkeys, logger)?;
Ok(())
}

View File

@ -8,8 +8,11 @@ use frost::Error;
use frost::Identifier;
use itertools::Itertools;
use std::collections::BTreeMap;
use std::fs;
use std::io::{BufRead, Write};
use crate::args::Args;
#[derive(Debug, PartialEq, Clone)]
pub struct Config {
pub min_signers: u16,
@ -34,41 +37,56 @@ fn validate_inputs(config: &Config) -> Result<(), Error> {
}
pub fn request_inputs(
args: &Args,
input: &mut impl BufRead,
logger: &mut impl Write,
) -> Result<Config, Box<dyn std::error::Error>> {
writeln!(logger, "The minimum number of signers: (2 or more)")?;
let config = if args.cli {
writeln!(logger, "The minimum number of signers: (2 or more)")?;
let mut min = String::new();
input.read_line(&mut min)?;
let mut min = String::new();
input.read_line(&mut min)?;
let min_signers = min
.trim()
.parse::<u16>()
.map_err(|_| Error::InvalidMinSigners)?;
let min_signers = min
.trim()
.parse::<u16>()
.map_err(|_| Error::InvalidMinSigners)?;
writeln!(logger, "The maximum number of signers: ")?;
writeln!(logger, "The maximum number of signers: ")?;
let mut max = String::new();
input.read_line(&mut max)?;
let max_signers = max
.trim()
.parse::<u16>()
.map_err(|_| Error::InvalidMaxSigners)?;
let mut max = String::new();
input.read_line(&mut max)?;
let max_signers = max
.trim()
.parse::<u16>()
.map_err(|_| Error::InvalidMaxSigners)?;
writeln!(
logger,
"Secret key (press enter to randomly generate a fresh one): "
)?;
writeln!(
logger,
"Secret key (press enter to randomly generate a fresh one): "
)?;
let mut secret_input = String::new();
input.read_line(&mut secret_input)?;
let secret = hex::decode(secret_input.trim()).map_err(|_| Error::MalformedSigningKey)?;
let mut secret_input = String::new();
input.read_line(&mut secret_input)?;
let secret = hex::decode(secret_input.trim()).map_err(|_| Error::MalformedSigningKey)?;
let config = Config {
min_signers,
max_signers,
secret,
Config {
min_signers,
max_signers,
secret,
}
} else {
let secret = hex::decode(args.key.clone().unwrap_or("".to_string()))
.map_err(|_| Error::MalformedSigningKey)?;
eprintln!(
"Generating {} shares with threshold {}...",
args.num_signers, args.threshold
);
Config {
min_signers: args.threshold,
max_signers: args.num_signers,
secret,
}
};
validate_inputs(&config)?;
@ -77,23 +95,39 @@ pub fn request_inputs(
}
pub fn print_values(
args: &Args,
keys: &BTreeMap<Identifier, SecretShare>,
pubkeys: &PublicKeyPackage,
logger: &mut dyn Write,
) -> Result<(), Box<dyn std::error::Error>> {
writeln!(
logger,
"Public key package:\n{}",
serde_json::to_string(pubkeys).unwrap()
)?;
for (k, v) in keys.iter().sorted_by_key(|x| x.0) {
writeln!(logger, "Participant: {}", hex::encode(k.serialize()))?;
if args.cli {
writeln!(
logger,
"Secret share:\n{}",
serde_json::to_string(v).unwrap()
"Public key package:\n{}",
serde_json::to_string(pubkeys)?
)?;
for (k, v) in keys.iter().sorted_by_key(|x| x.0) {
writeln!(logger, "Participant: {}", hex::encode(k.serialize()))?;
writeln!(
logger,
"Secret share:\n{}",
serde_json::to_string(v).unwrap()
)?;
}
} else {
fs::write(&args.public_key_package, serde_json::to_vec(pubkeys)?)?;
eprintln!("Public key package written to {}", &args.public_key_package);
for (i, (k, v)) in keys.iter().sorted_by_key(|x| x.0).enumerate() {
let path = str::replace(&args.key_package, "{}", format!("{}", i + 1).as_str());
fs::write(&path, serde_json::to_vec(v)?)?;
eprintln!(
"Key package for participant {} written to {}",
hex::encode(k.serialize()),
&path
);
}
}
Ok(())

View File

@ -1,3 +1,4 @@
pub mod args;
pub mod cli;
pub mod inputs;
pub mod trusted_dealer_keygen;

View File

@ -1,15 +1,20 @@
#[cfg(test)]
mod tests;
// TODO: fix and restore tests
// #[cfg(test)]
// mod tests;
use std::io;
use trusted_dealer::cli::cli;
use clap::Parser;
use trusted_dealer::{args::Args, cli::cli};
// TODO: Update to use exit codes
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let mut reader = Box::new(io::stdin().lock());
let mut logger = io::stdout();
cli(&mut reader, &mut logger)?;
cli(&args, &mut reader, &mut logger)?;
Ok(())
}