2024-10-24 06:36:20 -07:00
|
|
|
use std::{
|
|
|
|
env,
|
|
|
|
error::Error,
|
|
|
|
io::{BufRead, Write},
|
2024-11-20 07:11:47 -08:00
|
|
|
rc::Rc,
|
2024-10-24 06:36:20 -07:00
|
|
|
};
|
|
|
|
|
2024-01-30 11:07:32 -08:00
|
|
|
use clap::Parser;
|
2024-10-24 06:36:20 -07:00
|
|
|
use eyre::eyre;
|
|
|
|
use frost_core::{
|
|
|
|
keys::{KeyPackage, SecretShare},
|
|
|
|
Ciphersuite,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::input::read_from_file_or_stdin;
|
2024-01-30 11:07:32 -08:00
|
|
|
|
|
|
|
#[derive(Parser, Debug, Default)]
|
|
|
|
#[command(author, version, about, long_about = None)]
|
|
|
|
pub struct Args {
|
2024-06-21 17:24:23 -07:00
|
|
|
#[arg(short = 'C', long, default_value = "ed25519")]
|
|
|
|
pub ciphersuite: String,
|
|
|
|
|
2024-02-09 09:29:26 -08:00
|
|
|
/// 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.
|
|
|
|
#[arg(long, default_value_t = false)]
|
|
|
|
pub cli: bool,
|
|
|
|
|
2024-05-17 05:30:38 -07:00
|
|
|
/// HTTP mode. If enabled, it will use HTTP communication with a
|
|
|
|
/// FROST server.
|
|
|
|
#[arg(long, default_value_t = false)]
|
|
|
|
pub http: bool,
|
|
|
|
|
2024-08-14 14:48:59 -07:00
|
|
|
/// The username to use in HTTP mode.
|
|
|
|
#[arg(short = 'u', long, default_value = "")]
|
|
|
|
pub username: String,
|
|
|
|
|
|
|
|
/// The password to use in HTTP mode. If specified, it will be read from the
|
|
|
|
/// environment variable with the given name.
|
|
|
|
#[arg(short = 'w', long, default_value = "")]
|
|
|
|
pub password: String,
|
|
|
|
|
2024-01-30 11:07:32 -08:00
|
|
|
/// Public key package to use. Can be a file with a JSON-encoded
|
2024-03-21 06:08:18 -07:00
|
|
|
/// package, or "". If the file does not exist or if "" is specified,
|
2024-01-30 11:07:32 -08:00
|
|
|
/// then it will be read from standard input.
|
|
|
|
#[arg(short = 'k', long, default_value = "key-package-1.json")]
|
|
|
|
pub key_package: String,
|
|
|
|
|
2024-02-21 06:20:11 -08:00
|
|
|
/// IP to connect to, if using online comms
|
|
|
|
#[arg(short, long, default_value = "127.0.0.1")]
|
2024-01-30 11:07:32 -08:00
|
|
|
pub ip: String,
|
|
|
|
|
2024-02-21 06:20:11 -08:00
|
|
|
/// Port to connect to, if using online comms
|
2024-01-30 11:07:32 -08:00
|
|
|
#[arg(short, long, default_value_t = 2744)]
|
|
|
|
pub port: u16,
|
2024-05-17 05:30:38 -07:00
|
|
|
|
|
|
|
/// Optional Session ID
|
|
|
|
#[arg(short, long, default_value = "")]
|
|
|
|
pub session_id: String,
|
2024-01-30 11:07:32 -08:00
|
|
|
}
|
2024-10-24 06:36:20 -07:00
|
|
|
|
2024-11-20 07:11:47 -08:00
|
|
|
#[derive(Clone)]
|
2024-10-24 06:36:20 -07:00
|
|
|
pub struct ProcessedArgs<C: Ciphersuite> {
|
|
|
|
/// 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 authentication token to use in HTTP mode; if not specified
|
|
|
|
/// it will login with `password`
|
|
|
|
pub authentication_token: Option<String>,
|
|
|
|
|
|
|
|
/// Key package to use.
|
|
|
|
pub key_package: KeyPackage<C>,
|
|
|
|
|
|
|
|
/// IP to bind to, if using socket comms.
|
|
|
|
/// IP to connect to, if using HTTP mode.
|
|
|
|
pub ip: String,
|
|
|
|
|
|
|
|
/// Port to bind to, if using socket comms.
|
|
|
|
/// Port to connect to, if using HTTP mode.
|
|
|
|
pub port: u16,
|
|
|
|
|
|
|
|
/// Optional Session ID
|
|
|
|
pub session_id: String,
|
2024-11-20 07:11:47 -08:00
|
|
|
|
|
|
|
/// The participant's communication private key. Specifying this along with
|
|
|
|
/// `comm_coordinator_pubkey_getter` enables encryption.
|
|
|
|
pub comm_privkey: Option<Vec<u8>>,
|
|
|
|
|
2024-12-18 15:17:57 -08:00
|
|
|
/// The participant's communication public key.
|
|
|
|
pub comm_pubkey: Option<Vec<u8>>,
|
|
|
|
|
|
|
|
/// A function that confirms that a public key from the server is trusted by
|
|
|
|
/// the user; returns the same public key.
|
2024-11-20 07:11:47 -08:00
|
|
|
// It is a `Rc<dyn Fn>` to make it easier to use;
|
|
|
|
// using `fn()` would preclude using closures and using generics would
|
|
|
|
// require a lot of code change for something simple.
|
|
|
|
#[allow(clippy::type_complexity)]
|
2024-12-18 15:17:57 -08:00
|
|
|
pub comm_coordinator_pubkey_getter: Option<Rc<dyn Fn(&Vec<u8>) -> Option<Vec<u8>>>>,
|
2024-10-24 06:36:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
read_password(&args.password)?
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
|
|
|
|
|
|
|
let bytes = read_from_file_or_stdin(input, output, "key package", &args.key_package)?;
|
|
|
|
|
|
|
|
let key_package = if let Ok(secret_share) = serde_json::from_str::<SecretShare<C>>(&bytes) {
|
|
|
|
KeyPackage::try_from(secret_share)?
|
|
|
|
} else {
|
|
|
|
// TODO: Improve error
|
|
|
|
serde_json::from_str::<KeyPackage<C>>(&bytes)?
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(ProcessedArgs {
|
|
|
|
cli: args.cli,
|
|
|
|
http: args.http,
|
|
|
|
username: args.username.clone(),
|
|
|
|
password,
|
|
|
|
key_package,
|
|
|
|
ip: args.ip.clone(),
|
|
|
|
port: args.port,
|
|
|
|
authentication_token: None,
|
|
|
|
session_id: args.session_id.clone(),
|
2024-11-20 07:11:47 -08:00
|
|
|
comm_privkey: None,
|
2024-12-18 15:17:57 -08:00
|
|
|
comm_pubkey: None,
|
2024-11-20 07:11:47 -08:00
|
|
|
comm_coordinator_pubkey_getter: None,
|
2024-10-24 06:36:20 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read_password(password_env_name: &str) -> Result<String, Box<dyn Error>> {
|
|
|
|
if password_env_name.is_empty() {
|
|
|
|
Ok(
|
|
|
|
rpassword::prompt_password("Password: ")
|
|
|
|
.map_err(|_| eyre!("Error reading password"))?,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
Ok(env::var(password_env_name).map_err(|_| eyre!("The password argument must specify the name of a environment variable containing the password"))?)
|
|
|
|
}
|
|
|
|
}
|