diff --git a/Cargo.lock b/Cargo.lock index df2702de..d46ff5e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1300,6 +1300,7 @@ dependencies = [ "snow", "stable-eyre", "tempfile", + "thiserror 2.0.11", "tokio", "toml", "trusted-dealer", diff --git a/coordinator/src/args.rs b/coordinator/src/args.rs index a3825a9b..b4ff9992 100644 --- a/coordinator/src/args.rs +++ b/coordinator/src/args.rs @@ -11,6 +11,7 @@ use eyre::eyre; use frost_core::{keys::PublicKeyPackage, Ciphersuite, Identifier}; use frost_rerandomized::Randomizer; +use frostd::PublicKey; use crate::input::read_from_file_or_stdin; @@ -86,7 +87,7 @@ pub struct ProcessedArgs { pub http: bool, /// Signers to use in HTTP mode, as a map of public keys to identifiers. - pub signers: HashMap, Identifier>, + pub signers: HashMap>, /// The number of participants. pub num_signers: u16, @@ -116,7 +117,7 @@ pub struct ProcessedArgs { pub comm_privkey: Option>, /// The coordinator's communication public key for HTTP mode. - pub comm_pubkey: Option>, + pub comm_pubkey: Option, } impl ProcessedArgs { diff --git a/coordinator/src/comms/http.rs b/coordinator/src/comms/http.rs index 1f3e18e0..3e0e9a81 100644 --- a/coordinator/src/comms/http.rs +++ b/coordinator/src/comms/http.rs @@ -43,7 +43,7 @@ pub enum SessionState { /// Commitments sent by participants so far, for each message being /// signed. commitments: HashMap, Vec>>, - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, }, /// Commitments have been sent by all participants. Coordinator can create /// SigningPackage and send to participants. Waiting for participants to @@ -54,7 +54,7 @@ pub enum SessionState { /// All commitments sent by participants, for each message being signed. commitments: HashMap, Vec>>, /// Pubkey -> Identifier mapping. - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, /// Signature shares sent by participants so far, for each message being /// signed. signature_shares: HashMap, Vec>>, @@ -74,7 +74,7 @@ impl SessionState { pub fn new( num_messages: usize, num_signers: usize, - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, ) -> Self { let args = SessionStateArgs { num_messages, @@ -113,7 +113,7 @@ impl SessionState { /// Handle commitments sent by a participant. fn handle_commitments( &mut self, - pubkey: Vec, + pubkey: PublicKey, commitments: Vec>, ) -> Result<(), Box> { if let SessionState::WaitingForCommitments { @@ -164,7 +164,7 @@ impl SessionState { ) -> Result< ( Vec, SigningCommitments>>, - HashMap, Identifier>, + HashMap>, ), Box, > { @@ -197,7 +197,7 @@ impl SessionState { /// Handle signature share sent by a participant. fn handle_signature_share( &mut self, - pubkey: Vec, + pubkey: PublicKey, signature_shares: Vec>, ) -> Result<(), Box> { if let SessionState::WaitingForSignatureShares { @@ -265,11 +265,11 @@ pub struct HTTPComms { access_token: Option, args: ProcessedArgs, state: SessionState, - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, // The "send" Noise objects by pubkey of recipients. - send_noise: Option, Noise>>, + send_noise: Option>, // The "receive" Noise objects by pubkey of senders. - recv_noise: Option, Noise>>, + recv_noise: Option>, _phantom: PhantomData, } @@ -295,7 +295,7 @@ impl HTTPComms { } // Encrypts a message for a given recipient. - fn encrypt(&mut self, recipient: &Vec, msg: Vec) -> Result, Box> { + fn encrypt(&mut self, recipient: &PublicKey, msg: Vec) -> Result, Box> { let noise_map = self .send_noise .as_mut() @@ -390,7 +390,7 @@ impl Comms for HTTPComms { .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.keys().cloned().map(PublicKey).collect(), + pubkeys: self.args.signers.keys().cloned().collect(), message_count: 1, }) .send() @@ -423,7 +423,7 @@ impl Comms for HTTPComms { let send_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(comm_participant_pubkey) + .remote_public_key(&comm_participant_pubkey.0) .build_initiator()?, ); let builder = snow::Builder::new( @@ -434,7 +434,7 @@ impl Comms for HTTPComms { let recv_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(comm_participant_pubkey) + .remote_public_key(&comm_participant_pubkey.0) .build_responder()?, ); send_noise_map.insert(comm_participant_pubkey.clone(), send_noise); @@ -506,7 +506,7 @@ impl Comms for HTTPComms { ) .json(&frostd::SendArgs { session_id: self.session_id.unwrap(), - recipients: vec![frostd::PublicKey(recipient.clone())], + recipients: vec![recipient.clone()], msg, }) .send() diff --git a/dkg/src/args.rs b/dkg/src/args.rs index 42e7911f..143b0809 100644 --- a/dkg/src/args.rs +++ b/dkg/src/args.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use clap::Parser; use frost_core::{Ciphersuite, Identifier}; +use frostd::PublicKey; #[derive(Parser, Debug, Default)] #[command(author, version, about, long_about = None)] @@ -30,7 +31,7 @@ pub struct ProcessedArgs { pub comm_privkey: Option>, /// The participant's communication public key for HTTP mode. - pub comm_pubkey: Option>, + pub comm_pubkey: Option, /// A function that confirms that a public key from the server is trusted by /// the user; returns the same public key. For HTTP mode. @@ -38,7 +39,7 @@ pub struct ProcessedArgs { // 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) -> Option>>>, + pub comm_participant_pubkey_getter: Option Option>>, /// The threshold to use for the shares pub min_signers: u16, @@ -48,7 +49,7 @@ pub struct ProcessedArgs { /// The list of pubkeys for the other participants. This is only required /// for the first participant who creates the DKG session. - pub participants: Vec>, + pub participants: Vec, /// Identifier to use for the participant. Only needed for CLI mode. pub identifier: Option>, diff --git a/dkg/src/cli.rs b/dkg/src/cli.rs index aaef9534..d0009c35 100644 --- a/dkg/src/cli.rs +++ b/dkg/src/cli.rs @@ -2,6 +2,7 @@ use eyre::eyre; use frost_core::keys::{KeyPackage, PublicKeyPackage}; use frost_core::{self as frost, Ciphersuite, Identifier}; +use frostd::PublicKey; use rand::thread_rng; use reddsa::frost::redpallas::keys::EvenY; use std::collections::HashMap; @@ -77,7 +78,7 @@ pub async fn cli_for_processed_args( ( KeyPackage, PublicKeyPackage, - HashMap, Identifier>, + HashMap>, ), Box, > { diff --git a/dkg/src/comms.rs b/dkg/src/comms.rs index 4ed0d6fb..5dbc0949 100644 --- a/dkg/src/comms.rs +++ b/dkg/src/comms.rs @@ -6,6 +6,7 @@ use frost_core::{ keys::dkg::{round1, round2}, Ciphersuite, }; +use frostd::PublicKey; use std::{ collections::{BTreeMap, HashMap}, @@ -46,5 +47,7 @@ pub trait Comms { ) -> Result, round2::Package>, Box>; /// Return the map of public keys to identifiers for all participants. - fn get_pubkey_identifier_map(&self) -> Result, Identifier>, Box>; + fn get_pubkey_identifier_map( + &self, + ) -> Result>, Box>; } diff --git a/dkg/src/comms/cli.rs b/dkg/src/comms/cli.rs index a20a138c..540aa4e9 100644 --- a/dkg/src/comms/cli.rs +++ b/dkg/src/comms/cli.rs @@ -9,6 +9,7 @@ use frost_core::Ciphersuite; use async_trait::async_trait; use frost::{keys::PublicKeyPackage, Identifier}; +use frostd::PublicKey; use std::collections::HashMap; use std::{ @@ -138,7 +139,9 @@ where Ok(received_round2_packages) } - fn get_pubkey_identifier_map(&self) -> Result, Identifier>, Box> { + fn get_pubkey_identifier_map( + &self, + ) -> Result>, Box> { Ok(Default::default()) } } diff --git a/dkg/src/comms/http.rs b/dkg/src/comms/http.rs index 6b51a08b..d869e7fa 100644 --- a/dkg/src/comms/http.rs +++ b/dkg/src/comms/http.rs @@ -34,7 +34,7 @@ pub enum SessionState { WaitingForRound1Packages { /// Pubkey -> Identifier mapping. This is set during the /// get_identifier() call of HTTPComms. - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, /// Round 1 Packages sent by participants so far. round1_packages: BTreeMap, round1::Package>, }, @@ -43,7 +43,7 @@ pub enum SessionState { /// for details. WaitingForRound1PackagesBroadcast { /// Pubkey -> Identifier mapping. - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, /// Original Round 1 Packages sent by the other participants. round1_packages: BTreeMap, round1::Package>, /// Broadcasted Round 1 Packages sent by the other participants, @@ -56,7 +56,7 @@ pub enum SessionState { /// participants to send their Round 2 Packages. WaitingForRound2Packages { /// Pubkey -> Identifier mapping. - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, /// Round 1 Packages sent by participants. round1_packages: BTreeMap, round1::Package>, /// Round 2 Packages sent by participants so far @@ -66,7 +66,7 @@ pub enum SessionState { /// fetched by this participant. Round2PackagesReady { /// Pubkey -> Identifier mapping. - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, /// Round 2 Packages sent by participants so far round2_packages: BTreeMap, round2::Package>, }, @@ -116,7 +116,7 @@ impl SessionState { /// Handle commitments sent by a participant. fn handle_round1_package( &mut self, - pubkey: Vec, + pubkey: PublicKey, round1_package: round1::Package, ) -> Result<(), Box> { if let SessionState::WaitingForRound1Packages { @@ -169,7 +169,7 @@ impl SessionState { /// [1]: https://eprint.iacr.org/2002/040.pdf fn handle_round1_package_broadcast( &mut self, - sender_pubkey: Vec, + sender_pubkey: PublicKey, self_identifier: Identifier, original_identifier: Identifier, round1_package: round1::Package, @@ -301,7 +301,7 @@ impl SessionState { /// Handle signature share sent by a participant. fn handle_round2_package( &mut self, - pubkey: Vec, + pubkey: PublicKey, round2_package: round2::Package, ) -> Result<(), Box> { if let SessionState::WaitingForRound2Packages { @@ -359,11 +359,11 @@ pub struct HTTPComms { args: ProcessedArgs, state: SessionState, identifier: Option>, - pubkeys: HashMap, Identifier>, + pubkeys: HashMap>, // The "send" Noise objects by pubkey of recipients. - send_noise: Option, Noise>>, + send_noise: Option>, // The "receive" Noise objects by pubkey of senders. - recv_noise: Option, Noise>>, + recv_noise: Option>, _phantom: PhantomData, } @@ -386,7 +386,7 @@ impl HTTPComms { } // Encrypts a message for a given recipient. - fn encrypt(&mut self, recipient: &Vec, msg: Vec) -> Result, Box> { + fn encrypt(&mut self, recipient: &PublicKey, msg: Vec) -> Result, Box> { let noise_map = self .send_noise .as_mut() @@ -481,13 +481,7 @@ impl Comms for HTTPComms { .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 - .participants - .iter() - .cloned() - .map(PublicKey) - .collect(), + pubkeys: self.args.participants.clone(), message_count: 1, }) .send() @@ -537,7 +531,7 @@ impl Comms for HTTPComms { .iter() .map(|p| { Ok(( - p.0.clone(), + p.clone(), Identifier::::derive(&[session_id.as_bytes(), &p.0[..]].concat())?, )) }) @@ -556,7 +550,7 @@ impl Comms for HTTPComms { // of the session ID and the communication public key. // This ensures the identifier is unique and that participants can // derive each other's identifiers. - let input = [session_id.as_bytes(), &comm_pubkey[..]].concat(); + let input = [session_id.as_bytes(), &comm_pubkey.0[..]].concat(); let identifier = Identifier::::derive(&input)?; self.identifier = Some(identifier); Ok((identifier, self.pubkeys.len() as u16)) @@ -589,7 +583,7 @@ impl Comms for HTTPComms { let send_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(&comm_participant_pubkey) + .remote_public_key(&comm_participant_pubkey.0) .build_initiator()?, ); let builder = snow::Builder::new( @@ -600,7 +594,7 @@ impl Comms for HTTPComms { let recv_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(&comm_participant_pubkey) + .remote_public_key(&comm_participant_pubkey.0) .build_responder()?, ); send_noise_map.insert(pubkey.clone(), send_noise); @@ -620,7 +614,7 @@ impl Comms for HTTPComms { .bearer_auth(self.access_token.as_ref().expect("was just set")) .json(&frostd::SendArgs { session_id: self.session_id.expect("set before"), - recipients: vec![PublicKey(pubkey.clone())], + recipients: vec![pubkey.clone()], msg, }) .send() @@ -679,7 +673,7 @@ impl Comms for HTTPComms { .bearer_auth(self.access_token.as_ref().expect("was just set")) .json(&frostd::SendArgs { session_id: self.session_id.expect("set before"), - recipients: vec![PublicKey(recipient_pubkey.clone())], + recipients: vec![recipient_pubkey.clone()], msg, }) .send() @@ -745,7 +739,7 @@ impl Comms for HTTPComms { .bearer_auth(self.access_token.as_ref().expect("was just set")) .json(&frostd::SendArgs { session_id: self.session_id.expect("set before"), - recipients: vec![PublicKey(pubkey.clone())], + recipients: vec![pubkey.clone()], msg, }) .send() @@ -814,7 +808,9 @@ impl Comms for HTTPComms { self.state.round2_packages() } - fn get_pubkey_identifier_map(&self) -> Result, Identifier>, Box> { + fn get_pubkey_identifier_map( + &self, + ) -> Result>, Box> { match &self.state { SessionState::Round2PackagesReady { pubkeys, .. } => Ok(pubkeys.clone()), _ => Err(eyre!("wrong state").into()), diff --git a/frost-client/Cargo.toml b/frost-client/Cargo.toml index 5c77862d..14afcd04 100644 --- a/frost-client/Cargo.toml +++ b/frost-client/Cargo.toml @@ -32,3 +32,4 @@ rand = { workspace = true } stable-eyre = { workspace = true } itertools = { workspace = true } xeddsa = { workspace = true } +thiserror = { workspace = true } diff --git a/frost-client/src/config.rs b/frost-client/src/config.rs index cb379fc1..74dacc47 100644 --- a/frost-client/src/config.rs +++ b/frost-client/src/config.rs @@ -8,6 +8,7 @@ use std::{ use eyre::{eyre, OptionExt}; use frost_core::{Ciphersuite, Identifier}; +use frostd::PublicKey; use serde::{Deserialize, Serialize}; use crate::{ciphersuite_helper::ciphersuite_helper, contact::Contact, write_atomic}; @@ -30,18 +31,18 @@ pub struct Config { } impl Config { - pub fn contact_by_pubkey(&self, pubkey: &[u8]) -> Result> { - if Some(pubkey) == self.communication_key.as_ref().map(|c| c.pubkey.as_slice()) { + pub fn contact_by_pubkey(&self, pubkey: &PublicKey) -> Result> { + if Some(pubkey) == self.communication_key.as_ref().map(|c| &c.pubkey) { return Ok(Contact { version: Some(0), name: "".to_string(), - pubkey: pubkey.to_vec(), + pubkey: pubkey.clone(), }); } Ok(self .contact .values() - .find(|c| c.pubkey == pubkey) + .find(|c| c.pubkey == *pubkey) .cloned() .ok_or_eyre("contact not found")?) } @@ -57,11 +58,7 @@ pub struct CommunicationKey { )] pub privkey: Vec, /// The public key. - #[serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - )] - pub pubkey: Vec, + pub pubkey: PublicKey, } /// A FROST group the user belongs to. @@ -106,17 +103,17 @@ impl Group { ); for participant in self.participant.values() { let contact = config.contact_by_pubkey(&participant.pubkey)?; - s += &format!("\t{}\t({})\n", contact.name, hex::encode(contact.pubkey)); + s += &format!("\t{}\t({})\n", contact.name, hex::encode(contact.pubkey.0)); } Ok(s) } /// Get a group participant by their pubkey. - pub fn participant_by_pubkey(&self, pubkey: &[u8]) -> Result> { + pub fn participant_by_pubkey(&self, pubkey: &PublicKey) -> Result> { Ok(self .participant .values() - .find(|p| p.pubkey == pubkey) + .find(|p| p.pubkey == *pubkey) .cloned() .ok_or_eyre("Participant not found")?) } @@ -132,11 +129,7 @@ pub struct Participant { )] pub identifier: Vec, /// The communication public key for the participant. - #[serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - )] - pub pubkey: Vec, + pub pubkey: PublicKey, } impl Participant { diff --git a/frost-client/src/contact.rs b/frost-client/src/contact.rs index 1565822a..6196f44a 100644 --- a/frost-client/src/contact.rs +++ b/frost-client/src/contact.rs @@ -1,6 +1,7 @@ use std::error::Error; use eyre::{eyre, OptionExt}; +use frostd::PublicKey; use serde::{Deserialize, Serialize}; use crate::{args::Command, config::Config}; @@ -15,11 +16,7 @@ pub struct Contact { /// Name of the contact. pub name: String, /// Public key of the contact. - #[serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - )] - pub pubkey: Vec, + pub pubkey: PublicKey, } impl Contact { @@ -29,7 +26,7 @@ impl Contact { format!( "Name: {}\nPublic Key: {}\n", self.name, - hex::encode(&self.pubkey) + hex::encode(&self.pubkey.0) ) } @@ -137,7 +134,7 @@ pub(crate) fn remove(args: &Command) -> Result<(), Box> { .contact .iter() .find_map(|(name, c)| { - if hex::encode(c.pubkey.clone()) == pubkey { + if hex::encode(&c.pubkey.0) == pubkey { Some(name.clone()) } else { None diff --git a/frost-client/src/coordinator.rs b/frost-client/src/coordinator.rs index 416625c6..b4010dec 100644 --- a/frost-client/src/coordinator.rs +++ b/frost-client/src/coordinator.rs @@ -10,6 +10,7 @@ use frost_core::keys::PublicKeyPackage; use frost_core::Ciphersuite; use frost_ed25519::Ed25519Sha512; use frost_rerandomized::RandomizedCiphersuite; +use frostd::PublicKey; use reddsa::frost::redpallas::PallasBlake2b512; use reqwest::Url; @@ -69,7 +70,7 @@ pub(crate) async fn run_for_ciphersuite( let signers = signers .iter() .map(|s| { - let pubkey = hex::decode(s)?.to_vec(); + let pubkey = PublicKey(hex::decode(s)?.to_vec()); let contact = group.participant_by_pubkey(&pubkey)?; Ok((pubkey, contact.identifier()?)) }) diff --git a/frost-client/src/dkg.rs b/frost-client/src/dkg.rs index 190043a5..709dd868 100644 --- a/frost-client/src/dkg.rs +++ b/frost-client/src/dkg.rs @@ -61,7 +61,7 @@ pub(crate) async fn dkg_for_ciphersuite, Box>>()?; // Add ourselves if not already in the list if !participants.is_empty() && !participants.contains(&comm_pubkey) { diff --git a/frost-client/src/init.rs b/frost-client/src/init.rs index 7f2d06aa..1b66efc0 100644 --- a/frost-client/src/init.rs +++ b/frost-client/src/init.rs @@ -20,7 +20,7 @@ pub(crate) async fn init(args: &Command) -> Result<(), Box> { let keypair = builder.generate_keypair().unwrap(); config.communication_key = Some(CommunicationKey { privkey: keypair.private.clone(), - pubkey: keypair.public.clone(), + pubkey: frostd::PublicKey(keypair.public.clone()), }); }; diff --git a/frost-client/src/session.rs b/frost-client/src/session.rs index 59556452..b3666d3b 100644 --- a/frost-client/src/session.rs +++ b/frost-client/src/session.rs @@ -103,7 +103,7 @@ pub(crate) async fn list(args: &Command) -> Result<(), Box> { let participants: Vec<_> = r .pubkeys .iter() - .map(|pubkey| config.contact_by_pubkey(&pubkey.0)) + .map(|pubkey| config.contact_by_pubkey(&pubkey)) .collect(); eprintln!("Session with ID {}", session_id); eprintln!( @@ -118,7 +118,7 @@ pub(crate) async fn list(args: &Command) -> Result<(), Box> { eprintln!( "\t{}\t({})", participant.name, - hex::encode(participant.pubkey) + hex::encode(&participant.pubkey.0) ); } else { eprintln!("\t(Unknown contact)"); diff --git a/frostd/src/functions.rs b/frostd/src/functions.rs index ba2aeaa8..011ad2c3 100644 --- a/frostd/src/functions.rs +++ b/frostd/src/functions.rs @@ -3,7 +3,7 @@ use uuid::Uuid; use xeddsa::{xed25519, Verify as _}; use crate::{ - state::{Session, SharedState}, + state::{Session, SessionParticipant, SharedState}, types::*, user::User, AppError, @@ -31,11 +31,11 @@ pub(crate) async fn login( Json(args): Json, ) -> Result, AppError> { // Check if the user sent the credentials - if args.signature.is_empty() || args.pubkey.is_empty() { + if args.signature.is_empty() || args.pubkey.0.is_empty() { return Err(AppError::InvalidArgument("signature or pubkey".into())); } - let pubkey = TryInto::<[u8; 32]>::try_into(args.pubkey.clone()) + let pubkey = TryInto::<[u8; 32]>::try_into(args.pubkey.0.clone()) .map_err(|_| AppError::InvalidArgument("pubkey".into()))?; let pubkey = xed25519::PublicKey(pubkey); let signature = TryInto::<[u8; 64]>::try_into(args.signature) @@ -94,7 +94,7 @@ pub(crate) async fn create_new_session( // Save session ID in global state for pubkey in &args.pubkeys { sessions_by_pubkey - .entry(pubkey.clone().0) + .entry(pubkey.clone()) .or_default() .insert(id); } @@ -105,7 +105,7 @@ pub(crate) async fn create_new_session( .insert(id); // Create Session object let session = Session { - pubkeys: args.pubkeys.into_iter().map(|p| p.0).collect(), + pubkeys: args.pubkeys.clone(), coordinator_pubkey: user.pubkey, message_count: args.message_count, queue: Default::default(), @@ -157,7 +157,7 @@ pub(crate) async fn get_session_info( Ok(Json(GetSessionInfoOutput { message_count: session.message_count, - pubkeys: session.pubkeys.iter().cloned().map(PublicKey).collect(), + pubkeys: session.pubkeys.clone(), coordinator_pubkey: session.coordinator_pubkey.clone(), })) } @@ -180,14 +180,17 @@ pub(crate) async fn send( .ok_or(AppError::SessionNotFound)?; let recipients = if args.recipients.is_empty() { - vec![Vec::new()] + vec![SessionParticipant::Coordinator] } else { - args.recipients.into_iter().map(|p| p.0).collect() + args.recipients + .into_iter() + .map(SessionParticipant::Participant) + .collect() }; - for pubkey in &recipients { + for recipient in &recipients { session .queue - .entry(pubkey.clone()) + .entry(recipient.clone()) .or_default() .push_back(Msg { sender: user.pubkey.clone(), @@ -217,22 +220,27 @@ pub(crate) async fn receive( .get(&args.session_id) .ok_or(AppError::SessionNotFound)?; - let pubkey = if user.pubkey == session.coordinator_pubkey && args.as_coordinator { - Vec::new() + let participant = if user.pubkey == session.coordinator_pubkey && args.as_coordinator { + SessionParticipant::Coordinator } else { - user.pubkey + SessionParticipant::Participant(user.pubkey) }; // If there are no new messages, we don't want to renew the timeout. // Thus only if there are new messages we drop the read-only lock // to get the write lock and re-insert the updated session. - let msgs = if session.queue.contains_key(&pubkey) { + let msgs = if session.queue.contains_key(&participant) { drop(sessions); let mut sessions = state.sessions.sessions.write().unwrap(); let mut session = sessions .remove(&args.session_id) .ok_or(AppError::SessionNotFound)?; - let msgs = session.queue.entry(pubkey).or_default().drain(..).collect(); + let msgs = session + .queue + .entry(participant) + .or_default() + .drain(..) + .collect(); sessions.insert(args.session_id, session); msgs } else { diff --git a/frostd/src/state.rs b/frostd/src/state.rs index 946e39a9..b3f6c3f6 100644 --- a/frostd/src/state.rs +++ b/frostd/src/state.rs @@ -10,7 +10,7 @@ use delay_map::{HashMapDelay, HashSetDelay}; use futures::{Stream, StreamExt as _}; use uuid::Uuid; -use crate::Msg; +use crate::{Msg, PublicKey}; /// How long a session stays open. const SESSION_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(60 * 60 * 24); @@ -35,17 +35,23 @@ impl Stream for RwLockStream<'_, T> { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SessionParticipant { + Coordinator, + Participant(PublicKey), +} + /// A particular signing session. #[derive(Debug)] pub struct Session { /// The public keys of the participants - pub(crate) pubkeys: Vec>, + pub(crate) pubkeys: Vec, /// The public key of the coordinator - pub(crate) coordinator_pubkey: Vec, + pub(crate) coordinator_pubkey: PublicKey, /// The number of messages being simultaneously signed. pub(crate) message_count: u8, /// The message queue. - pub(crate) queue: HashMap, VecDeque>, + pub(crate) queue: HashMap>, } /// The global state of the server. @@ -53,14 +59,14 @@ pub struct Session { pub struct AppState { pub(crate) sessions: SessionState, pub(crate) challenges: Arc>>, - pub(crate) access_tokens: Arc>>>, + pub(crate) access_tokens: Arc>>, } #[derive(Debug, Default)] pub struct SessionState { /// Mapping of signing sessions by UUID. pub(crate) sessions: Arc>>, - pub(crate) sessions_by_pubkey: Arc, HashSet>>>, + pub(crate) sessions_by_pubkey: Arc>>>, } impl SessionState { diff --git a/frostd/src/types.rs b/frostd/src/types.rs index 67bdd212..8f69547b 100644 --- a/frostd/src/types.rs +++ b/frostd/src/types.rs @@ -9,17 +9,6 @@ pub struct Error { pub msg: String, } -#[derive(Debug, Serialize, Deserialize)] -pub struct RegisterArgs { - pub username: String, - pub password: String, - #[serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - )] - pub pubkey: Vec, -} - #[derive(Debug, Serialize, Deserialize)] pub struct ChallengeArgs {} @@ -31,11 +20,7 @@ pub struct ChallengeOutput { #[derive(Debug, Serialize, Deserialize)] pub struct KeyLoginArgs { pub challenge: Uuid, - #[serde( - serialize_with = "serdect::slice::serialize_hex_lower_or_bin", - deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" - )] - pub pubkey: Vec, + pub pubkey: PublicKey, #[serde( serialize_with = "serdect::slice::serialize_hex_lower_or_bin", deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" @@ -84,10 +69,10 @@ pub struct GetSessionInfoArgs { pub struct GetSessionInfoOutput { pub message_count: u8, pub pubkeys: Vec, - pub coordinator_pubkey: Vec, + pub coordinator_pubkey: PublicKey, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(transparent)] pub struct PublicKey( #[serde( @@ -118,7 +103,7 @@ pub struct SendArgs { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Msg { - pub sender: Vec, + pub sender: PublicKey, #[serde( serialize_with = "serdect::slice::serialize_hex_lower_or_bin", deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" diff --git a/frostd/src/user.rs b/frostd/src/user.rs index 0e5195da..bc128882 100644 --- a/frostd/src/user.rs +++ b/frostd/src/user.rs @@ -7,13 +7,13 @@ use axum_extra::{ }; use uuid::Uuid; -use crate::{state::SharedState, AppError}; +use crate::{state::SharedState, AppError, PublicKey}; /// An User #[derive(Debug)] #[allow(dead_code)] pub(crate) struct User { - pub(crate) pubkey: Vec, + pub(crate) pubkey: PublicKey, pub(crate) current_token: Uuid, } diff --git a/frostd/tests/integration_tests.rs b/frostd/tests/integration_tests.rs index 5ddf3161..b3979fbb 100644 --- a/frostd/tests/integration_tests.rs +++ b/frostd/tests/integration_tests.rs @@ -89,7 +89,7 @@ async fn test_main_router< .post("/login") .json(&frostd::KeyLoginArgs { challenge: alice_challenge, - pubkey: alice_keypair.public.clone(), + pubkey: frostd::PublicKey(alice_keypair.public.clone()), signature: alice_signature.to_vec(), }) .await; @@ -104,7 +104,7 @@ async fn test_main_router< .post("/login") .json(&frostd::KeyLoginArgs { challenge: bob_challenge, - pubkey: bob_keypair.public.clone(), + pubkey: frostd::PublicKey(bob_keypair.public.clone()), signature: bob_signature.to_vec(), }) .await; @@ -182,6 +182,7 @@ async fn test_main_router< let pubkey_identifier_map = comm_pubkeys .into_iter() .cloned() + .map(frostd::PublicKey) .zip(key_packages.keys().take(2).copied()) .collect::>(); let mut coordinator_state = SessionState::::new(2, 2, pubkey_identifier_map); @@ -242,7 +243,7 @@ async fn test_main_router< .authorization_bearer(alice_token) .json(&frostd::SendArgs { session_id, - recipients: usernames.keys().cloned().map(frostd::PublicKey).collect(), + recipients: usernames.keys().cloned().collect(), msg: serde_json::to_vec(&send_signing_package_args)?, }) .await; @@ -464,7 +465,7 @@ async fn test_http() -> Result<(), Box> { .post("https://127.0.0.1:2744/login") .json(&frostd::KeyLoginArgs { challenge: alice_challenge, - pubkey: alice_keypair.public.clone(), + pubkey: frostd::PublicKey(alice_keypair.public.clone()), signature: alice_signature.to_vec(), }) .send() @@ -528,7 +529,7 @@ async fn test_http() -> Result<(), Box> { .post("https://127.0.0.1:2744/login") .json(&frostd::KeyLoginArgs { challenge: bob_challenge, - pubkey: bob_keypair.public.clone(), + pubkey: frostd::PublicKey(bob_keypair.public), signature: bob_signature.to_vec(), }) .send() diff --git a/participant/src/args.rs b/participant/src/args.rs index c4f1903d..f009de1a 100644 --- a/participant/src/args.rs +++ b/participant/src/args.rs @@ -11,6 +11,7 @@ use frost_core::{ keys::{KeyPackage, SecretShare}, Ciphersuite, }; +use frostd::PublicKey; use crate::input::read_from_file_or_stdin; @@ -74,7 +75,7 @@ pub struct ProcessedArgs { pub comm_privkey: Option>, /// The participant's communication public key for HTTP mode. - pub comm_pubkey: Option>, + pub comm_pubkey: Option, /// A function that confirms that a public key from the server is trusted by /// the user; returns the same public key. For HTTP mode. @@ -82,7 +83,7 @@ pub struct ProcessedArgs { // 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_coordinator_pubkey_getter: Option) -> Option>>>, + pub comm_coordinator_pubkey_getter: Option Option>>, } impl ProcessedArgs { diff --git a/participant/src/comms/http.rs b/participant/src/comms/http.rs index e866f0f1..70cf48c7 100644 --- a/participant/src/comms/http.rs +++ b/participant/src/comms/http.rs @@ -274,7 +274,7 @@ where let send_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(&comm_coordinator_pubkey) + .remote_public_key(&comm_coordinator_pubkey.0) .build_initiator()?, ); let builder = snow::Builder::new( @@ -285,7 +285,7 @@ where let recv_noise = Noise::new( builder .local_private_key(comm_privkey) - .remote_public_key(&comm_coordinator_pubkey) + .remote_public_key(&comm_coordinator_pubkey.0) .build_responder()?, ); self.send_noise = Some(send_noise);