From 9eb1f7e5b1c9890e06e12ae962a08a397108a99b Mon Sep 17 00:00:00 2001 From: Conrado Gouvea Date: Fri, 13 Dec 2024 20:37:23 -0300 Subject: [PATCH] server: misc cleanups --- Cargo.lock | 1 + coordinator/src/args.rs | 2 +- coordinator/src/comms/http.rs | 15 +++++++------- participant/src/args.rs | 2 +- participant/src/comms/http.rs | 2 +- server/Cargo.toml | 1 + server/README.md | 29 ++++++++++---------------- server/src/functions.rs | 34 ++++++++++++++++--------------- server/src/lib.rs | 7 ++++--- server/src/main.rs | 10 ++++++++- server/src/state.rs | 2 -- server/src/types.rs | 16 +++++++++------ server/tests/integration_tests.rs | 18 +++++++++------- 13 files changed, 76 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b02d2fd1..9129098d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2976,6 +2976,7 @@ dependencies = [ "frost-core", "frost-ed25519", "frost-rerandomized", + "hex", "password-auth", "rand", "rcgen", diff --git a/coordinator/src/args.rs b/coordinator/src/args.rs index 52d9a9bb..8a0138a4 100644 --- a/coordinator/src/args.rs +++ b/coordinator/src/args.rs @@ -75,7 +75,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 9cc053ab..66a27128 100644 --- a/coordinator/src/comms/http.rs +++ b/coordinator/src/comms/http.rs @@ -265,7 +265,6 @@ pub struct HTTPComms { host_port: String, session_id: Option, access_token: Option, - num_signers: u16, args: ProcessedArgs, state: SessionState, pubkeys: HashMap, Identifier>, @@ -284,7 +283,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(), @@ -342,7 +340,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 @@ -370,7 +368,7 @@ impl Comms for HTTPComms { self.client .post(format!("{}/login", self.host_port)) .json(&server::KeyLoginArgs { - uuid: challenge, + challenge, pubkey: self .args .comm_pubkey @@ -391,8 +389,12 @@ impl Comms for HTTPComms { .post(format!("{}/create_new_session", self.host_port)) .bearer_auth(self.access_token.as_ref().expect("was just set")) .json(&server::CreateNewSessionArgs { - pubkeys: self.args.signers.clone(), - num_signers, + pubkeys: self + .args + .signers + .iter() + .map(|p| server::PublicKey(p.clone())) + .collect(), message_count: 1, }) .send() @@ -407,7 +409,6 @@ impl Comms for HTTPComms { ); } self.session_id = Some(r.session_id); - self.num_signers = num_signers; // If encryption is enabled, create the Noise objects (self.send_noise, self.recv_noise) = if let ( diff --git a/participant/src/args.rs b/participant/src/args.rs index 83e7a550..f47a3ab8 100644 --- a/participant/src/args.rs +++ b/participant/src/args.rs @@ -42,7 +42,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 172aaa40..f79ee4da 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 1017e114..0ddec370 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" password-auth = "1.0.0" rand = "0.8" rcgen = "0.13.1" diff --git a/server/README.md b/server/README.md index 84c659a4..a336ef86 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: -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` +To compile and run: -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`. +1. Clone the repo. Run `git clone https://github.com/ZcashFoundation/frost-zcash-demo.git` +2. Run `cargo build --release --bin server` +3. Run `./target/release/server -h` to learn about the command line arguments. -## TODO +You will need to specify a TLS certificate and key with the `--tls-cert` +and `--tls-key` arguments. -- 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 b11613fc..406ea619 100644 --- a/server/src/functions.rs +++ b/server/src/functions.rs @@ -11,7 +11,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, @@ -26,7 +26,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, @@ -53,11 +53,11 @@ pub(crate) async fn login( ) })?; pubkey - .verify(args.uuid.as_bytes(), &signature) + .verify(args.challenge.as_bytes(), &signature) .map_err(|_| AppError(StatusCode::UNAUTHORIZED, eyre!("invalid signature").into()))?; let mut challenges = state.challenges.write().unwrap(); - if !challenges.remove(&args.uuid) { + if !challenges.remove(&args.challenge) { return Err(AppError( StatusCode::UNAUTHORIZED, eyre!("invalid challenge").into(), @@ -76,7 +76,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, @@ -90,7 +90,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, @@ -112,15 +112,14 @@ pub(crate) async fn create_new_session( for pubkey in &args.pubkeys { state .sessions_by_pubkey - .entry(pubkey.clone()) + .entry(pubkey.clone().0) .or_default() .insert(id); } // Create Session object let session = Session { - pubkeys: args.pubkeys, + 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(), }; @@ -132,7 +131,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, @@ -149,7 +148,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, @@ -178,16 +177,19 @@ pub(crate) async fn get_session_info( ))?; Ok(Json(GetSessionInfoOutput { - num_signers: session.num_signers, message_count: session.message_count, - pubkeys: session.pubkeys.clone(), + pubkeys: session + .pubkeys + .iter() + .map(|p| PublicKey(p.clone())) + .collect(), coordinator_pubkey: session.coordinator_pubkey.clone(), })) } /// 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, @@ -225,7 +227,7 @@ pub(crate) async fn send( /// Implement the recv 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 receive( State(state): State, user: User, @@ -254,7 +256,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 1ac9c706..b3323d22 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -48,9 +48,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 6746341d..0da64dde 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 5e4adb8e..3f86b109 100644 --- a/server/src/state.rs +++ b/server/src/state.rs @@ -20,8 +20,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 set of identifiers for the session. // pub(crate) identifiers: BTreeSet, /// The number of messages being simultaneously signed. diff --git a/server/src/types.rs b/server/src/types.rs index c9ec0808..f00bab4b 100644 --- a/server/src/types.rs +++ b/server/src/types.rs @@ -26,7 +26,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" @@ -57,8 +57,7 @@ pub struct LoginArgs { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CreateNewSessionArgs { - pub pubkeys: Vec>, - pub num_signers: u16, + pub pubkeys: Vec, pub message_count: u8, } @@ -79,13 +78,12 @@ pub struct GetSessionInfoArgs { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GetSessionInfoOutput { - pub num_signers: u16, pub message_count: u8, - pub pubkeys: Vec>, + pub pubkeys: Vec, pub coordinator_pubkey: Vec, } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] #[serde(transparent)] pub struct PublicKey( #[serde( @@ -95,6 +93,12 @@ pub struct PublicKey( pub Vec, ); +impl std::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&hex::encode(&self.0)) + } +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SendArgs { pub session_id: Uuid, diff --git a/server/tests/integration_tests.rs b/server/tests/integration_tests.rs index 06840931..ccbe40a4 100644 --- a/server/tests/integration_tests.rs +++ b/server/tests/integration_tests.rs @@ -88,7 +88,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(), }) @@ -103,7 +103,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(), }) @@ -119,8 +119,10 @@ async fn test_main_router< .post("/create_new_session") .authorization_bearer(alice_token) .json(&server::CreateNewSessionArgs { - pubkeys: vec![alice_keypair.public.clone(), bob_keypair.public.clone()], - num_signers: 2, + pubkeys: vec![ + server::PublicKey(alice_keypair.public.clone()), + server::PublicKey(bob_keypair.public.clone()), + ], message_count: 2, }) .await; @@ -459,7 +461,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(), }) @@ -476,9 +478,11 @@ async fn test_http() -> Result<(), Box> { .post("https://127.0.0.1:2744/create_new_session") .bearer_auth(access_token) .json(&server::CreateNewSessionArgs { - pubkeys: vec![alice_keypair.public.clone(), bob_keypair.public.clone()], + pubkeys: vec![ + server::PublicKey(alice_keypair.public.clone()), + server::PublicKey(bob_keypair.public.clone()), + ], message_count: 1, - num_signers: 2, }) .send() .await?;