Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make server act as "aggregator" #265

Merged
merged 3 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions coordinator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

[dependencies]
async-trait = "0.1.81"
derivative = "2.2.0"
eyre = "0.6.12"
frost-core = { version = "2.0.0-rc.0", features = ["serde"] }
frost-rerandomized = { version = "2.0.0-rc.0", features = ["serde"] }
Expand All @@ -16,6 +17,7 @@ hex = { version = "0.4", features = ["serde"] }
thiserror = "1.0"
rand = "0.8"
serde_json = "1.0"
serdect = { version = "0.2.0" }
itertools = "0.13.0"
exitcode = "1.1.2"
clap = { version = "4.5.13", features = ["derive"] }
Expand Down
193 changes: 181 additions & 12 deletions coordinator/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
use std::{
env,
error::Error,
fs,
io::{BufRead, Write},
};

use clap::Parser;
use eyre::eyre;

use frost_core::{keys::PublicKeyPackage, Ciphersuite};
use frost_rerandomized::Randomizer;

#[derive(Parser, Debug, Default)]
use crate::input::read_from_file_or_stdin;

#[derive(Clone, Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(short = 'C', long, default_value = "ed25519")]
Expand Down Expand Up @@ -32,26 +45,31 @@ pub struct Args {
#[arg(short = 'S', long, value_delimiter = ',')]
pub signers: Vec<String>,

/// The number of participants. If 0, will prompt for a value.
/// The number of participants. If `signers` is specified, it will use the
/// length of `signers`. Otherwise, if 0, it will prompt for a value.
#[arg(short = 'n', long, default_value_t = 0)]
pub num_signers: u16,

/// Public key package to use. Can be a file with a JSON-encoded
/// package, or "". If the file does not exist or if "" is specified,
/// package, or "-". If the file does not exist or if "-" is specified,
/// then it will be read from standard input.
#[arg(short = 'P', long, default_value = "public-key-package.json")]
pub public_key_package: String,

/// The message to sign. Can be a file with the raw message, or "". If ""
/// is specified, then it will be read from standard input as a hex string.
#[arg(short = 'm', long, default_value = "")]
pub message: String,
/// The messages to sign. Each instance can be a file with the raw message,
/// "" or "-". If "" or "-" is specified, then it will be read from standard
/// input as a hex string. If none are passed, a single one will be read
/// from standard input as a hex string.
#[arg(short = 'm', long)]
pub message: Vec<String>,

/// The randomizer to use. Can be a file with the raw randomizer, empty, or
/// "-". If empty, a random one will be generated. If "-" is specified, then
/// it will be read from standard input as a hex string.
#[arg(short = 'r', long, default_value = "")]
pub randomizer: String,
/// The randomizers to use. Each instance can be a file with the raw
/// randomizer, "" or "-". If "" or "-" is specified, then it will be read
/// from standard input as a hex string. If none are passed, random ones
/// will be generated. If one or more are passed, the number should match
/// the `message` parameter.
#[arg(short = 'r', long)]
pub randomizer: Vec<String>,

/// Where to write the generated raw bytes signature. If "-", the
/// human-readable hex-string is printed to stdout.
Expand All @@ -66,3 +84,154 @@ pub struct Args {
#[arg(short, long, default_value_t = 2744)]
pub port: u16,
}

#[derive(Clone, Debug)]
pub struct ProcessedArgs<C: Ciphersuite> {
pub ciphersuite: String,

/// CLI mode. If enabled, it will prompt for inputs from stdin
/// and print values to stdout, ignoring other flags.
/// If false, socket communication is enabled.
pub cli: bool,

/// HTTP mode. If enabled, it will use HTTP communication with a
/// FROST server.
pub http: bool,

/// The username to use in HTTP mode.
pub username: String,

/// The (actual) password to use in HTTP mode.
pub password: String,

/// The comma-separated usernames of the signers to use in HTTP mode.
/// If HTTP mode is enabled and this is empty, then the session ID
/// will be printed and will have to be shared manually.
pub signers: Vec<String>,

/// The number of participants.
pub num_signers: u16,

/// Public key package to use.
pub public_key_package: PublicKeyPackage<C>,

/// The messages to sign.
pub messages: Vec<Vec<u8>>,

/// The randomizers to use.
pub randomizers: Vec<Randomizer<C>>,

/// Where to write the generated raw bytes signature. If "-", the
/// human-readable hex-string is printed to stdout.
pub signature: String,

/// IP to bind to, if using online comms
pub ip: String,

/// Port to bind to, if using online comms
pub port: u16,
}

impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
/// Create a ProcessedArgs from a Args.
///
/// Validates inputs and reads/parses arguments.
pub fn new(
args: &Args,
input: &mut dyn BufRead,
output: &mut dyn Write,
) -> Result<Self, Box<dyn Error>> {
let password = if args.http {
env::var(&args.password).map_err(|_| eyre!("The password argument must specify the name of a environment variable containing the password"))?
} else {
String::new()
};

let num_signers = if !args.signers.is_empty() {
args.signers.len() as u16
} else if args.num_signers == 0 {
writeln!(output, "The number of participants: ")?;

let mut participants = String::new();
input.read_line(&mut participants)?;
participants.trim().parse::<u16>()?
} else {
args.num_signers
};

let out = read_from_file_or_stdin(
input,
output,
"public key package",
&args.public_key_package,
)?;

let public_key_package: PublicKeyPackage<C> = serde_json::from_str(&out)?;

let messages = if args.message.is_empty() {
writeln!(output, "The message to be signed (hex encoded)")?;
let mut msg = String::new();
input.read_line(&mut msg)?;
vec![hex::decode(msg.trim())?]
} else {
args.message
.iter()
.map(|filename| {
let msg = if filename == "-" || filename.is_empty() {
writeln!(output, "The message to be signed (hex encoded)")?;
let mut msg = String::new();
input.read_line(&mut msg)?;
hex::decode(msg.trim())?
} else {
eprintln!("Reading message from {}...", &filename);
fs::read(filename)?
};
Ok(msg)
})
.collect::<Result<_, Box<dyn Error>>>()?
};

println!("Processing randomizer {:?}", args.randomizer);
let randomizers = if args.ciphersuite == "redpallas" {
if args.randomizer.is_empty() {
Vec::new()
} else {
args.randomizer
.iter()
.map(|filename| {
let randomizer = if filename == "-" || filename.is_empty() {
writeln!(output, "Enter the randomizer (hex string):")?;
let mut randomizer = String::new();
input.read_line(&mut randomizer)?;
let bytes = hex::decode(randomizer.trim())?;
frost_rerandomized::Randomizer::deserialize(&bytes)?
} else {
eprintln!("Reading randomizer from {}...", &filename);
let bytes = fs::read(filename)?;
frost_rerandomized::Randomizer::deserialize(&bytes)?
};
Ok(randomizer)
})
.collect::<Result<_, Box<dyn Error>>>()?
}
} else {
Vec::new()
};

Ok(ProcessedArgs {
ciphersuite: args.ciphersuite.clone(),
cli: args.cli,
http: args.http,
username: args.username.clone(),
password,
signers: args.signers.clone(),
num_signers,
public_key_package,
messages,
randomizers,
signature: args.signature.clone(),
ip: args.ip.clone(),
port: args.port,
})
}
}
24 changes: 7 additions & 17 deletions coordinator/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,50 @@ use std::io::{BufRead, Write};
use frost_rerandomized::RandomizedCiphersuite;

use crate::args::Args;
use crate::args::ProcessedArgs;
use crate::comms::cli::CLIComms;
use crate::comms::http::HTTPComms;
use crate::comms::socket::SocketComms;
use crate::comms::Comms;
use crate::step_1::step_1;
use crate::step_2::step_2;
use crate::step_3::request_randomizer;
use crate::step_3::step_3;

pub async fn cli<C: RandomizedCiphersuite + 'static>(
args: &Args,
reader: &mut impl BufRead,
logger: &mut impl Write,
) -> Result<(), Box<dyn std::error::Error>> {
let pargs = ProcessedArgs::<C>::new(args, reader, logger)?;

writeln!(logger, "\n=== STEP 1: CHOOSE PARTICIPANTS ===\n")?;

let mut comms: Box<dyn Comms<C>> = if args.cli {
Box::new(CLIComms::new())
} else if args.http {
Box::new(HTTPComms::new(args)?)
Box::new(HTTPComms::new(&pargs)?)
} else {
Box::new(SocketComms::new(args))
};

let participants_config = step_1(args, &mut *comms, reader, logger).await?;
let participants_config = step_1(&pargs, &mut *comms, reader, logger).await?;

writeln!(
logger,
"=== STEP 2: CHOOSE MESSAGE AND GENERATE COMMITMENT PACKAGE ===\n"
)?;

let signing_package = step_2(
args,
reader,
logger,
participants_config.commitments.clone(),
)?;

let randomizer = if args.ciphersuite == "redpallas" {
Some(request_randomizer(args, reader, logger, &signing_package)?)
} else {
None
};
let signing_package = step_2(&pargs, logger, participants_config.commitments.clone())?;

writeln!(logger, "=== STEP 3: BUILD GROUP SIGNATURE ===\n")?;

step_3(
args,
&pargs,
&mut *comms,
reader,
logger,
participants_config,
&signing_package,
randomizer,
)
.await?;

Expand Down
Loading