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

frost-client: allow removing contacts and groups #400

Merged
merged 3 commits into from
Dec 26, 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
643 changes: 3 additions & 640 deletions Cargo.lock

Large diffs are not rendered by default.

36 changes: 4 additions & 32 deletions coordinator/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,6 @@ pub struct Args {
#[arg(long, default_value_t = false)]
pub http: bool,

/// 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,

/// 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.
Expand Down Expand Up @@ -99,16 +90,6 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// 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>,

/// The comma-separated keys 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.
Expand Down Expand Up @@ -138,15 +119,15 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// Port to connect to, if using HTTP mode.
pub port: u16,

/// The coordinator's communication private key. Specifying this along with
/// `comm_participant_pubkey_getter` enables encryption.
/// The coordinator's communication private key for HTTP mode.
pub comm_privkey: Option<Vec<u8>>,

/// The coordinator's communication public key.
/// The coordinator's communication public key for HTTP mode.
pub comm_pubkey: Option<Vec<u8>>,

/// A function that confirms if the public key of a participant is in the
/// user's contact book, returning the same public key, or None if not.
/// user's contact book, returning the same public key, or None if not. For
/// HTTP mode.
// 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.
Expand All @@ -163,12 +144,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
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 num_signers = if !args.signers.is_empty() {
args.signers.len() as u16
} else if args.num_signers == 0 {
Expand Down Expand Up @@ -204,8 +179,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
Ok(ProcessedArgs {
cli: args.cli,
http: args.http,
username: args.username.clone(),
password,
signers,
num_signers,
public_key_package,
Expand All @@ -214,7 +187,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
signature: args.signature.clone(),
ip: args.ip.clone(),
port: args.port,
authentication_token: None,
comm_privkey: None,
comm_pubkey: None,
comm_participant_pubkey_getter: None,
Expand Down
83 changes: 48 additions & 35 deletions coordinator/src/comms/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,11 @@ pub struct HTTPComms<C: Ciphersuite> {
client: reqwest::Client,
host_port: String,
session_id: Option<Uuid>,
access_token: String,
access_token: Option<String>,
num_signers: u16,
args: ProcessedArgs<C>,
state: SessionState<C>,
pubkeys: HashMap<Vec<u8>, Identifier<C>>,
should_logout: bool,
// The "send" Noise objects by pubkey of recipients.
send_noise: Option<HashMap<Vec<u8>, Noise>>,
// The "receive" Noise objects by pubkey of senders.
Expand All @@ -286,12 +285,11 @@ impl<C: Ciphersuite> HTTPComms<C> {
client,
host_port: format!("http://{}:{}", args.ip, args.port),
session_id: None,
access_token: args.authentication_token.clone().unwrap_or_default(),
access_token: None,
num_signers: 0,
args: args.clone(),
state: SessionState::new(args.messages.len(), args.num_signers as usize),
pubkeys: Default::default(),
should_logout: args.authentication_token.is_none(),
send_noise: None,
recv_noise: None,
_phantom: Default::default(),
Expand Down Expand Up @@ -370,29 +368,30 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
);
let signature: [u8; 64] = privkey.sign(challenge.as_bytes(), &mut rng);

self.access_token = self
.client
.post(format!("{}/key_login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string();
self.access_token = Some(
self.client
.post(format!("{}/login", self.host_port))
.json(&server::KeyLoginArgs {
uuid: challenge,
pubkey: self
.args
.comm_pubkey
.clone()
.ok_or_eyre("comm_pubkey must be specified")?,
signature: signature.to_vec(),
})
.send()
.await?
.json::<server::LoginOutput>()
.await?
.access_token
.to_string(),
);

let r = self
.client
.post(format!("{}/create_new_session", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::CreateNewSessionArgs {
pubkeys: self.args.signers.iter().cloned().map(PublicKey).collect(),
num_signers,
Expand Down Expand Up @@ -460,7 +459,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let r = self
.client
.post(format!("{}/receive", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&server::ReceiveArgs {
session_id: r.session_id,
as_coordinator: true,
Expand Down Expand Up @@ -511,7 +510,11 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let _r = self
.client
.post(format!("{}/send", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::SendArgs {
session_id: self.session_id.unwrap(),
recipients: vec![server::PublicKey(recipient.clone())],
Expand All @@ -529,7 +532,11 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let r = self
.client
.post(format!("{}/receive", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::ReceiveArgs {
session_id: self.session_id.unwrap(),
as_coordinator: true,
Expand All @@ -553,21 +560,27 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let _r = self
.client
.post(format!("{}/close_session", self.host_port))
.bearer_auth(&self.access_token)
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.json(&server::CloseSessionArgs {
session_id: self.session_id.unwrap(),
})
.send()
.await?;

if self.should_logout {
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(&self.access_token)
.send()
.await?;
}
let _r = self
.client
.post(format!("{}/logout", self.host_port))
.bearer_auth(
self.access_token
.as_ref()
.expect("must have been set before"),
)
.send()
.await?;

let signature_shares = self.state.signature_shares()?;

Expand Down
55 changes: 23 additions & 32 deletions frost-client/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,9 @@ pub(crate) struct Args {

#[derive(Subcommand, Clone)]
pub(crate) enum Command {
/// Initializes the user, generating a communication key pair and optionally
/// registering with a FROST server. The key pair and additional information
/// are saved to the config file. You can rerun the command to register
/// in other servers; the communication key pair will not be regenerated.
Init {
/// The username to use when registering, if desired.
#[arg(short, long)]
username: Option<String>,
/// The server URL to use, if desired.
#[arg(short, long)]
server_url: Option<String>,
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
config: Option<String>,
},
/// Logs the user on the server and saves the returned authentication token
/// Initializes the user, generating a communication key pair and saving
/// to the config file.
Login {
/// The username to use when logging in.
#[arg(short, long)]
username: String,
/// The server URL to use.
#[arg(short, long)]
server_url: String,
Init {
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
Expand All @@ -45,10 +23,6 @@ pub(crate) enum Command {
/// The name to use when exporting.
#[arg(short, long)]
name: String,
/// The server URL for which to export a contact. You can use a
/// substring of the URL.
#[arg(short, long)]
server_url: Option<String>,
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
Expand All @@ -70,6 +44,16 @@ pub(crate) enum Command {
#[arg(short, long)]
config: Option<String>,
},
/// Remove a contact from the user's address book.
RemoveContact {
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
config: Option<String>,
/// The public key of the contact to remove (list with `contacts`).
#[arg(short, long)]
pubkey: String,
},
TrustedDealer {
/// The path to the config file to manage.
///
Expand All @@ -86,10 +70,6 @@ pub(crate) enum Command {
/// The comma-separated name of each participant.
#[arg(short = 'N', long, value_delimiter = ',')]
names: Vec<String>,
/// The comma-separated username of each participant in the same order
/// as `names`. Note: these won't be checked in the server.
#[arg(short, long, value_delimiter = ',')]
usernames: Vec<String>,
/// The server URL, if desired. Note that this does not connect to the
/// server; it will just associated the server URL with the group in the
/// config file.
Expand All @@ -111,6 +91,17 @@ pub(crate) enum Command {
#[arg(short, long)]
config: Option<String>,
},
/// Remove a group from the config.
RemoveGroup {
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
#[arg(short, long)]
config: Option<String>,
/// The group to remove, identified by the group public key (use
/// `groups` to list)
#[arg(short, long)]
group: String,
},
Coordinator {
/// The path to the config file to manage. If not specified, it uses
/// $HOME/.local/frost/credentials.toml
Expand Down
27 changes: 1 addition & 26 deletions frost-client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ pub struct Config {
#[serde(skip)]
path: Option<PathBuf>,
pub version: u8,
/// The registry of servers the user has registered into, keyed by server
/// URL.
#[serde(default)]
pub registry: BTreeMap<String, Registry>,
/// The communication key pair for the user.
pub communication_key: Option<CommunicationKey>,
/// The address book of the user, keyed by each contact's name.
Expand All @@ -41,25 +37,6 @@ impl Config {
.cloned()
.ok_or_eyre("contact not found")?)
}

pub fn username_by_server_url(&self, server_url: &str) -> Result<String, Box<dyn Error>> {
Ok(self
.registry
.get(server_url)
.ok_or_eyre("Not logged in in the giver server")?
.username
.clone())
}
}

/// A registry entry. Note that the server URL is not in the struct;
/// it is the key in the `registry` map in Config.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Registry {
/// The authentication token, if the user is logged in.
pub token: Option<String>,
/// The username of the user
pub username: String,
}

/// The communication key pair for the user.
Expand Down Expand Up @@ -117,7 +94,7 @@ impl Group {
);
for participant in self.participant.values() {
let contact = config.contact_by_pubkey(&participant.pubkey)?;
s += &format!("\t{}\n", contact.name);
s += &format!("\t{} ({})\n", contact.name, hex::encode(contact.pubkey));
}
Ok(s)
}
Expand All @@ -138,8 +115,6 @@ pub struct Participant {
deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec"
)]
pub pubkey: Vec<u8>,
/// The username of the participant in the server, if any.
pub username: Option<String>,
}

impl Config {
Expand Down
Loading