From d7b6ab7644c463452541f23387c6ed472b65a896 Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Thu, 26 Dec 2024 19:03:16 -0300 Subject: [PATCH] server: misc cleanups (#407) --- coordinator/src/args.rs | 2 +- coordinator/src/comms/http.rs | 8 ++------ frost-client/src/session.rs | 4 ++-- participant/src/args.rs | 2 +- participant/src/comms/http.rs | 2 +- server/Cargo.toml | 2 +- server/README.md | 29 +++++++++++------------------ server/src/functions.rs | 26 ++++++++++++-------------- server/src/lib.rs | 7 ++++--- server/src/main.rs | 10 +++++++++- server/src/state.rs | 2 -- server/src/types.rs | 4 +--- server/tests/integration_tests.rs | 10 ++++------ 13 files changed, 49 insertions(+), 59 deletions(-) diff --git a/coordinator/src/args.rs b/coordinator/src/args.rs index 77adb44..f7485b6 100644 --- a/coordinator/src/args.rs +++ b/coordinator/src/args.rs @@ -70,7 +70,7 @@ pub struct Args { /// Port to bind to, if using socket comms. /// Port to connect to, if using HTTP mode. - #[arg(short, long, default_value_t = 2744)] + #[arg(short, long, default_value_t = 443)] pub port: u16, } diff --git a/coordinator/src/comms/http.rs b/coordinator/src/comms/http.rs index 84458ee..e512620 100644 --- a/coordinator/src/comms/http.rs +++ b/coordinator/src/comms/http.rs @@ -267,7 +267,6 @@ pub struct HTTPComms { host_port: String, session_id: Option, access_token: Option, - num_signers: u16, args: ProcessedArgs, state: SessionState, pubkeys: HashMap, Identifier>, @@ -286,7 +285,6 @@ impl HTTPComms { host_port: format!("https://{}:{}", args.ip, args.port), session_id: None, access_token: None, - num_signers: 0, args: args.clone(), state: SessionState::new(args.messages.len(), args.num_signers as usize), pubkeys: Default::default(), @@ -340,7 +338,7 @@ impl Comms for HTTPComms { _input: &mut dyn BufRead, _output: &mut dyn Write, _pub_key_package: &PublicKeyPackage, - num_signers: u16, + _num_signers: u16, ) -> Result, SigningCommitments>, Box> { let mut rng = thread_rng(); let challenge = self @@ -368,7 +366,7 @@ impl Comms for HTTPComms { self.client .post(format!("{}/login", self.host_port)) .json(&server::KeyLoginArgs { - uuid: challenge, + challenge, pubkey: self .args .comm_pubkey @@ -390,7 +388,6 @@ impl Comms for HTTPComms { .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, message_count: 1, }) .send() @@ -405,7 +402,6 @@ impl Comms for HTTPComms { ); } self.session_id = Some(r.session_id); - self.num_signers = num_signers; let (Some(comm_privkey), Some(comm_participant_pubkey_getter)) = ( &self.args.comm_privkey, diff --git a/frost-client/src/session.rs b/frost-client/src/session.rs index c51aa31..2cbb5b3 100644 --- a/frost-client/src/session.rs +++ b/frost-client/src/session.rs @@ -66,7 +66,7 @@ pub(crate) async fn list(args: &Command) -> Result<(), Box> { let access_token = client .post(format!("{}/login", host_port)) .json(&server::KeyLoginArgs { - uuid: challenge, + challenge, pubkey: comm_pubkey.clone(), signature: signature.to_vec(), }) @@ -102,7 +102,7 @@ pub(crate) async fn list(args: &Command) -> Result<(), Box> { let participants: Vec<_> = r .pubkeys .iter() - .map(|pubkey| config.contact_by_pubkey(pubkey)) + .map(|pubkey| config.contact_by_pubkey(&pubkey.0)) .collect(); eprintln!("Session with ID {}", session_id); eprintln!( diff --git a/participant/src/args.rs b/participant/src/args.rs index 4bda847..c4f1903 100644 --- a/participant/src/args.rs +++ b/participant/src/args.rs @@ -37,7 +37,7 @@ pub struct Args { pub ip: String, /// Port to connect to, if using online comms - #[arg(short, long, default_value_t = 2744)] + #[arg(short, long, default_value_t = 443)] pub port: u16, /// Optional Session ID diff --git a/participant/src/comms/http.rs b/participant/src/comms/http.rs index 33f8ecd..a5ca75c 100644 --- a/participant/src/comms/http.rs +++ b/participant/src/comms/http.rs @@ -203,7 +203,7 @@ where self.client .post(format!("{}/login", self.host_port)) .json(&server::KeyLoginArgs { - uuid: challenge, + challenge, pubkey: self .args .comm_pubkey diff --git a/server/Cargo.toml b/server/Cargo.toml index d3d3af7..6029809 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -16,6 +16,7 @@ derivative = "2.2.0" eyre = "0.6.11" frost-core = { version = "2.0.0", features = ["serde"] } frost-rerandomized = { version = "2.0.0-rc.0", features = ["serde"] } +hex = "0.4" rand = "0.8" rcgen = "0.13.1" serde = { version = "1.0", features = ["derive"] } @@ -32,7 +33,6 @@ xeddsa = "1.0.2" futures-util = "0.3.31" futures = "0.3.31" thiserror = "2.0.3" -hex = "0.4.3" [dev-dependencies] axum-test = "16.4.0" diff --git a/server/README.md b/server/README.md index 84c659a..a336ef8 100755 --- a/server/README.md +++ b/server/README.md @@ -1,14 +1,12 @@ # FROST Server - -This is a HTTP server that allow clients (Coordinator and Participants) to -run FROST without needing to directly connect to one another. +This is a JSON-HTTPS server that allow FROST clients (Coordinator and +Participants) to run FROST without needing to directly connect to one another. ## Status ⚠ -This is a prototype which is NOT SECURE since messages are not encrypted nor -authenticated. DO NOT USE this for anything other than testing. +This project has not being audited. ## Usage @@ -17,19 +15,14 @@ NOTE: This is for demo purposes only and should not be used in production. You will need to have [Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) installed. -To run: +To compile and run: + 1. Clone the repo. Run `git clone https://github.com/ZcashFoundation/frost-zcash-demo.git` -2. Run `cargo install` -3. Run `cargo run --bin server` +2. Run `cargo build --release --bin server` +3. Run `./target/release/server -h` to learn about the command line arguments. -You can specify the IP and port to bind to using `--ip` and `--port`, e.g. -`cargo run --bin server -- --ip 127.0.0.1 --port 2744`. +You will need to specify a TLS certificate and key with the `--tls-cert` +and `--tls-key` arguments. -## TODO - -- Add specific error codes -- Remove frost-specific types (when data is encrypted) -- Session timeouts -- Encryption/authentication -- DoS protections and other production-ready requirements -- \ No newline at end of file +For more details on using and deploying, refer to the [ZF FROST +Book](https://frost.zfnd.org/). diff --git a/server/src/functions.rs b/server/src/functions.rs index 350cc76..866f749 100644 --- a/server/src/functions.rs +++ b/server/src/functions.rs @@ -10,7 +10,7 @@ use crate::{ }; /// Implement the challenge API. -#[tracing::instrument(ret, err(Debug), skip(state, _args))] +#[tracing::instrument(level = "debug", err(Debug), skip(state, _args))] pub(crate) async fn challenge( State(state): State, Json(_args): Json, @@ -25,7 +25,7 @@ pub(crate) async fn challenge( } /// Implement the key_login API. -#[tracing::instrument(ret, err(Debug), skip(state, args))] +#[tracing::instrument(level = "debug", err(Debug), skip(state, args))] pub(crate) async fn login( State(state): State, Json(args): Json, @@ -41,11 +41,11 @@ pub(crate) async fn login( let signature = TryInto::<[u8; 64]>::try_into(args.signature) .map_err(|_| AppError::InvalidArgument("signature".into()))?; pubkey - .verify(args.uuid.as_bytes(), &signature) + .verify(args.challenge.as_bytes(), &signature) .map_err(|_| AppError::Unauthorized)?; let mut challenges = state.challenges.write().unwrap(); - if !challenges.remove(&args.uuid) { + if !challenges.remove(&args.challenge) { return Err(AppError::Unauthorized); } drop(challenges); @@ -61,7 +61,7 @@ pub(crate) async fn login( } /// Implement the logout API. -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn logout( State(state): State, user: User, @@ -75,7 +75,7 @@ pub(crate) async fn logout( } /// Implement the create_new_session API. -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn create_new_session( State(state): State, user: User, @@ -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.0.clone()) + .entry(pubkey.clone().0) .or_default() .insert(id); } @@ -102,7 +102,6 @@ pub(crate) async fn create_new_session( let session = Session { pubkeys: args.pubkeys.into_iter().map(|p| p.0).collect(), coordinator_pubkey: user.pubkey, - num_signers: args.num_signers, message_count: args.message_count, queue: Default::default(), }; @@ -114,7 +113,7 @@ pub(crate) async fn create_new_session( } /// Implement the create_new_session API. -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn list_sessions( State(state): State, user: User, @@ -130,7 +129,7 @@ pub(crate) async fn list_sessions( } /// Implement the get_session_info API -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn get_session_info( State(state): State, user: User, @@ -152,7 +151,6 @@ pub(crate) async fn get_session_info( .ok_or(AppError::SessionNotFound)?; Ok(Json(GetSessionInfoOutput { - num_signers: session.num_signers, message_count: session.message_count, pubkeys: session.pubkeys.iter().cloned().map(PublicKey).collect(), coordinator_pubkey: session.coordinator_pubkey.clone(), @@ -161,7 +159,7 @@ pub(crate) async fn get_session_info( /// Implement the send API // TODO: get identifier from channel rather from arguments -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn send( State(state): State, user: User, @@ -197,7 +195,7 @@ pub(crate) async fn send( } /// Implement the recv API -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn receive( State(state): State, user: User, @@ -240,7 +238,7 @@ pub(crate) async fn receive( } /// Implement the close_session API. -#[tracing::instrument(ret, err(Debug), skip(state, user))] +#[tracing::instrument(level = "debug", ret, err(Debug), skip(state, user))] pub(crate) async fn close_session( State(state): State, user: User, diff --git a/server/src/lib.rs b/server/src/lib.rs index d30ef77..1269752 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -49,9 +49,10 @@ pub async fn run(args: &Args) -> Result<(), Box> { if args.no_tls_very_insecure { tracing::warn!( - "starting an INSECURE HTTP server. This should be done only for \ - testing or if you are providing TLS/HTTPS with a separate \ - mechanism (e.g. reverse proxy such as nginx)" + "starting an INSECURE HTTP server at {}. This should be done only \ + for testing or if you are providing TLS/HTTPS with a separate \ + mechanism (e.g. reverse proxy such as nginx)", + addr, ); let listener = tokio::net::TcpListener::bind(addr).await?; Ok(axum::serve(listener, app).await?) diff --git a/server/src/main.rs b/server/src/main.rs index 6746341..0da64dd 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,12 +1,20 @@ use clap::Parser; use server::args::Args; use server::run; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<(), Box> { let args = Args::parse(); // initialize tracing - tracing_subscriber::fmt::init(); + tracing_subscriber::fmt() + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .from_env_lossy(), + ) + .init(); tracing::event!(tracing::Level::INFO, "server running"); run(&args).await } diff --git a/server/src/state.rs b/server/src/state.rs index 7f39789..946e39a 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -42,8 +42,6 @@ pub struct Session { pub(crate) pubkeys: Vec>, /// The public key of the coordinator pub(crate) coordinator_pubkey: Vec, - /// The number of signers in the session. - pub(crate) num_signers: u16, /// The number of messages being simultaneously signed. pub(crate) message_count: u8, /// The message queue. diff --git a/server/src/types.rs b/server/src/types.rs index 2ac1975..2920b5e 100644 --- a/server/src/types.rs +++ b/server/src/types.rs @@ -32,7 +32,7 @@ pub struct ChallengeOutput { #[derive(Debug, Serialize, Deserialize)] pub struct KeyLoginArgs { - pub uuid: Uuid, + pub challenge: Uuid, #[serde( serialize_with = "serdect::slice::serialize_hex_lower_or_bin", deserialize_with = "serdect::slice::deserialize_hex_or_bin_vec" @@ -64,7 +64,6 @@ pub struct LoginArgs { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CreateNewSessionArgs { pub pubkeys: Vec, - pub num_signers: u16, pub message_count: u8, } @@ -85,7 +84,6 @@ pub struct GetSessionInfoArgs { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GetSessionInfoOutput { - pub num_signers: u16, pub message_count: u8, pub pubkeys: Vec, pub coordinator_pubkey: Vec, diff --git a/server/tests/integration_tests.rs b/server/tests/integration_tests.rs index d5dea01..598216d 100644 --- a/server/tests/integration_tests.rs +++ b/server/tests/integration_tests.rs @@ -89,7 +89,7 @@ async fn test_main_router< let res = server .post("/login") .json(&server::KeyLoginArgs { - uuid: alice_challenge, + challenge: alice_challenge, pubkey: alice_keypair.public.clone(), signature: alice_signature.to_vec(), }) @@ -104,7 +104,7 @@ async fn test_main_router< let res = server .post("/login") .json(&server::KeyLoginArgs { - uuid: bob_challenge, + challenge: bob_challenge, pubkey: bob_keypair.public.clone(), signature: bob_signature.to_vec(), }) @@ -124,7 +124,6 @@ async fn test_main_router< server::PublicKey(alice_keypair.public.clone()), server::PublicKey(bob_keypair.public.clone()), ], - num_signers: 2, message_count: 2, }) .await; @@ -463,7 +462,7 @@ async fn test_http() -> Result<(), Box> { let r = client .post("https://127.0.0.1:2744/login") .json(&server::KeyLoginArgs { - uuid: alice_challenge, + challenge: alice_challenge, pubkey: alice_keypair.public.clone(), signature: alice_signature.to_vec(), }) @@ -485,7 +484,6 @@ async fn test_http() -> Result<(), Box> { server::PublicKey(bob_keypair.public.clone()), ], message_count: 1, - num_signers: 2, }) .send() .await?; @@ -528,7 +526,7 @@ async fn test_http() -> Result<(), Box> { let r = client .post("https://127.0.0.1:2744/login") .json(&server::KeyLoginArgs { - uuid: bob_challenge, + challenge: bob_challenge, pubkey: bob_keypair.public.clone(), signature: bob_signature.to_vec(), })