Skip to content

Commit

Permalink
coordinator, participant: don't send identifier; get it from channel …
Browse files Browse the repository at this point in the history
…authentication
  • Loading branch information
conradoplg committed Jan 8, 2025
1 parent 9484deb commit 16b4306
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 132 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

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

[dependencies]
async-trait = "0.1.83"
derivative = "2.2.0"
eyre = "0.6.12"
frost-core = { version = "2.0.0", features = ["serde"] }
frost-rerandomized = { version = "2.0.0-rc.0", features = ["serde"] }
Expand Down
28 changes: 5 additions & 23 deletions coordinator/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use std::{
collections::HashMap,
env,
error::Error,
fs,
io::{BufRead, Write},
rc::Rc,
};

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

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

use crate::input::read_from_file_or_stdin;
Expand Down Expand Up @@ -85,10 +85,8 @@ pub struct ProcessedArgs<C: Ciphersuite> {
/// FROST server.
pub http: bool,

/// 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.
pub signers: Vec<Vec<u8>>,
/// Signers to use in HTTP mode, as a map of public keys to identifiers.
pub signers: HashMap<Vec<u8>, Identifier<C>>,

/// The number of participants.
pub num_signers: u16,
Expand Down Expand Up @@ -119,15 +117,6 @@ pub struct ProcessedArgs<C: Ciphersuite> {

/// 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. 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.
#[allow(clippy::type_complexity)]
pub comm_participant_pubkey_getter: Option<Rc<dyn Fn(&Vec<u8>) -> Option<Vec<u8>>>>,
}

impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
Expand Down Expand Up @@ -158,12 +147,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
&args.public_key_package,
)?;

let signers = args
.signers
.iter()
.map(|s| Ok(hex::decode(s)?.to_vec()))
.collect::<Result<_, Box<dyn Error>>>()?;

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

let messages = read_messages(&args.message, output, input)?;
Expand All @@ -174,7 +157,7 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
Ok(ProcessedArgs {
cli: args.cli,
http: false,
signers,
signers: HashMap::new(),
num_signers,
public_key_package,
messages,
Expand All @@ -184,7 +167,6 @@ impl<C: Ciphersuite + 'static> ProcessedArgs<C> {
port: args.port,
comm_privkey: None,
comm_pubkey: None,
comm_participant_pubkey_getter: None,
})
}
}
Expand Down
90 changes: 42 additions & 48 deletions coordinator/src/comms/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ use frost_core::{
Identifier, SigningPackage,
};

use frostd::{
Msg, PublicKey, SendCommitmentsArgs, SendSignatureSharesArgs, SendSigningPackageArgs, Uuid,
};
use frostd::{Msg, PublicKey, SendSigningPackageArgs, Uuid};
use participant::comms::http::Noise;
use rand::thread_rng;
use xeddsa::{xed25519, Sign as _};
Expand All @@ -36,8 +34,7 @@ pub struct SessionStateArgs {
///
/// This can be used by a Coordinator to help maitain state and handle
/// messages from the Participants.
#[derive(derivative::Derivative)]
#[derivative(Debug)]
#[derive(Debug)]
pub enum SessionState<C: Ciphersuite> {
/// Waiting for participants to send their commitments.
WaitingForCommitments {
Expand Down Expand Up @@ -74,15 +71,19 @@ pub enum SessionState<C: Ciphersuite> {

impl<C: Ciphersuite> SessionState<C> {
/// Create a new SessionState for the given number of messages and signers.
pub fn new(num_messages: usize, num_signers: usize) -> Self {
pub fn new(
num_messages: usize,
num_signers: usize,
pubkeys: HashMap<Vec<u8>, Identifier<C>>,
) -> Self {
let args = SessionStateArgs {
num_messages,
num_signers,
};
Self::WaitingForCommitments {
args,
commitments: Default::default(),
pubkeys: Default::default(),
pubkeys,
}
}

Expand All @@ -95,12 +96,12 @@ impl<C: Ciphersuite> SessionState<C> {
pub fn recv(&mut self, msg: Msg) -> Result<(), Box<dyn Error>> {
match self {
SessionState::WaitingForCommitments { .. } => {
let send_commitments_args: SendCommitmentsArgs<C> =
let send_commitments_args: Vec<SigningCommitments<C>> =
serde_json::from_slice(&msg.msg)?;
self.handle_commitments(msg.sender, send_commitments_args)?;
}
SessionState::WaitingForSignatureShares { .. } => {
let send_signature_shares_args: SendSignatureSharesArgs<C> =
let send_signature_shares_args: Vec<SignatureShare<C>> =
serde_json::from_slice(&msg.msg)?;
self.handle_signature_share(msg.sender, send_signature_shares_args)?;
}
Expand All @@ -113,34 +114,31 @@ impl<C: Ciphersuite> SessionState<C> {
fn handle_commitments(
&mut self,
pubkey: Vec<u8>,
send_commitments_args: SendCommitmentsArgs<C>,
commitments: Vec<SigningCommitments<C>>,
) -> Result<(), Box<dyn Error>> {
if let SessionState::WaitingForCommitments {
args,
commitments,
pubkeys: usernames,
commitments: commitments_map,
pubkeys,
} = self
{
if send_commitments_args.commitments.len() != args.num_messages {
if commitments.len() != args.num_messages {
return Err(eyre!("wrong number of commitments").into());
}
let identifier = *pubkeys.get(&pubkey).ok_or(eyre!("unknown participant"))?;

// Add commitment to map.
// Currently ignores the possibility of overwriting previous values
// (it seems better to ignore overwrites, which could be caused by
// poor networking connectivity leading to retries)
commitments.insert(
send_commitments_args.identifier,
send_commitments_args.commitments,
);
usernames.insert(pubkey, send_commitments_args.identifier);
commitments_map.insert(identifier, commitments);

// If complete, advance to next state
if commitments.len() == args.num_signers {
if commitments_map.len() == args.num_signers {
*self = SessionState::WaitingForSignatureShares {
args: args.clone(),
commitments: commitments.clone(),
pubkeys: usernames.clone(),
commitments: commitments_map.clone(),
pubkeys: pubkeys.clone(),
signature_shares: Default::default(),
}
}
Expand Down Expand Up @@ -199,37 +197,35 @@ impl<C: Ciphersuite> SessionState<C> {
/// Handle signature share sent by a participant.
fn handle_signature_share(
&mut self,
_username: Vec<u8>,
send_signature_shares_args: SendSignatureSharesArgs<C>,
pubkey: Vec<u8>,
signature_shares: Vec<SignatureShare<C>>,
) -> Result<(), Box<dyn Error>> {
if let SessionState::WaitingForSignatureShares {
args,
commitments,
signature_shares,
..
signature_shares: signature_shares_map,
pubkeys,
} = self
{
if send_signature_shares_args.signature_share.len() != args.num_messages {
if signature_shares.len() != args.num_messages {
return Err(eyre!("wrong number of signature shares").into());
}
if !commitments.contains_key(&send_signature_shares_args.identifier) {
let identifier = *pubkeys.get(&pubkey).ok_or(eyre!("unknown participant"))?;
if !commitments.contains_key(&identifier) {
return Err(eyre!("invalid identifier").into());
}

// Currently ignoring the possibility of overwriting previous values
// (it seems better to ignore overwrites, which could be caused by
// poor networking connectivity leading to retries)
signature_shares.insert(
send_signature_shares_args.identifier,
send_signature_shares_args.signature_share,
);
signature_shares_map.insert(identifier, signature_shares);
// If complete, advance to next state
if signature_shares.keys().cloned().collect::<HashSet<_>>()
if signature_shares_map.keys().cloned().collect::<HashSet<_>>()
== commitments.keys().cloned().collect::<HashSet<_>>()
{
*self = SessionState::SignatureSharesReady {
args: args.clone(),
signature_shares: signature_shares.clone(),
signature_shares: signature_shares_map.clone(),
}
}
Ok(())
Expand Down Expand Up @@ -286,7 +282,11 @@ impl<C: Ciphersuite> HTTPComms<C> {
session_id: None,
access_token: None,
args: args.clone(),
state: SessionState::new(args.messages.len(), args.num_signers as usize),
state: SessionState::new(
args.messages.len(),
args.num_signers as usize,
args.signers.clone(),
),
pubkeys: Default::default(),
send_noise: None,
recv_noise: None,
Expand Down Expand Up @@ -387,7 +387,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
.post(format!("{}/create_new_session", self.host_port))
.bearer_auth(self.access_token.as_ref().expect("was just set"))
.json(&frostd::CreateNewSessionArgs {
pubkeys: self.args.signers.iter().cloned().map(PublicKey).collect(),
pubkeys: self.args.signers.keys().cloned().map(PublicKey).collect(),
message_count: 1,
})
.send()
Expand All @@ -403,21 +403,15 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
}
self.session_id = Some(r.session_id);

let (Some(comm_privkey), Some(comm_participant_pubkey_getter)) = (
&self.args.comm_privkey,
&self.args.comm_participant_pubkey_getter,
) else {
return Err(
eyre!("comm_privkey and comm_participant_pubkey_getter must be specified").into(),
);
let Some(comm_privkey) = &self.args.comm_privkey else {
return Err(eyre!("comm_privkey must be specified").into());
};

// If encryption is enabled, create the Noise objects

let mut send_noise_map = HashMap::new();
let mut recv_noise_map = HashMap::new();
for pubkey in &self.args.signers {
let comm_participant_pubkey = comm_participant_pubkey_getter(pubkey).ok_or_eyre("A participant in specified FROST session is not registered in the coordinator's address book")?;
for comm_participant_pubkey in self.args.signers.keys() {
let builder = snow::Builder::new(
"Noise_K_25519_ChaChaPoly_BLAKE2s"
.parse()
Expand All @@ -426,7 +420,7 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let send_noise = Noise::new(
builder
.local_private_key(comm_privkey)
.remote_public_key(&comm_participant_pubkey)
.remote_public_key(comm_participant_pubkey)
.build_initiator()?,
);
let builder = snow::Builder::new(
Expand All @@ -437,11 +431,11 @@ impl<C: Ciphersuite + 'static> Comms<C> for HTTPComms<C> {
let recv_noise = Noise::new(
builder
.local_private_key(comm_privkey)
.remote_public_key(&comm_participant_pubkey)
.remote_public_key(comm_participant_pubkey)
.build_responder()?,
);
send_noise_map.insert(pubkey.clone(), send_noise);
recv_noise_map.insert(pubkey.clone(), recv_noise);
send_noise_map.insert(comm_participant_pubkey.clone(), send_noise);
recv_noise_map.insert(comm_participant_pubkey.clone(), recv_noise);
}
self.send_noise = Some(send_noise_map);
self.recv_noise = Some(recv_noise_map);
Expand Down
18 changes: 18 additions & 0 deletions frost-client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{
};

use eyre::{eyre, OptionExt};
use frost_core::{Ciphersuite, Identifier};
use serde::{Deserialize, Serialize};

use crate::{ciphersuite_helper::ciphersuite_helper, contact::Contact, write_atomic};
Expand Down Expand Up @@ -102,6 +103,16 @@ impl Group {
}
Ok(s)
}

/// Get a group participant by their pubkey.
pub fn participant_by_pubkey(&self, pubkey: &[u8]) -> Result<Participant, Box<dyn Error>> {
Ok(self
.participant
.values()
.find(|p| p.pubkey == pubkey)
.cloned()
.ok_or_eyre("Participant not found")?)
}
}

/// A FROST group participant.
Expand All @@ -121,6 +132,13 @@ pub struct Participant {
pub pubkey: Vec<u8>,
}

impl Participant {
/// Return the parsed identifier for the participant.
pub fn identifier<C: Ciphersuite>(&self) -> Result<Identifier<C>, Box<dyn std::error::Error>> {
Ok(Identifier::<C>::deserialize(&self.identifier)?)
}
}

impl Config {
/// Returns the default path of the config
/// ($HOME/.config/frost/credentials.toml in Linux) if `path` is None,
Expand Down
17 changes: 7 additions & 10 deletions frost-client/src/coordinator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::error::Error;
use std::rc::Rc;

use coordinator::cli::cli_for_processed_args;
use eyre::eyre;
Expand Down Expand Up @@ -68,11 +68,14 @@ pub(crate) async fn run_for_ciphersuite<C: RandomizedCiphersuite + 'static>(

let signers = signers
.iter()
.map(|s| Ok(hex::decode(s)?.to_vec()))
.collect::<Result<Vec<_>, Box<dyn Error>>>()?;
.map(|s| {
let pubkey = hex::decode(s)?.to_vec();
let contact = group.participant_by_pubkey(&pubkey)?;
Ok((pubkey, contact.identifier()?))
})
.collect::<Result<HashMap<_, _>, Box<dyn Error>>>()?;
let num_signers = signers.len() as u16;

let group_participants = group.participant.clone();
let pargs = coordinator::args::ProcessedArgs {
cli: false,
http: true,
Expand Down Expand Up @@ -102,12 +105,6 @@ pub(crate) async fn run_for_ciphersuite<C: RandomizedCiphersuite + 'static>(
.pubkey
.clone(),
),
comm_participant_pubkey_getter: Some(Rc::new(move |participant_pubkey| {
group_participants
.values()
.find(|p| p.pubkey == *participant_pubkey)
.map(|p| p.pubkey.clone())
})),
};

cli_for_processed_args(pargs, &mut input, &mut output).await?;
Expand Down
Loading

0 comments on commit 16b4306

Please sign in to comment.