From 022340ec951f45ebe4087d4eba3c8663062b6611 Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Tue, 19 Sep 2023 15:46:17 -0400 Subject: [PATCH] feat: added the ability to test a connection --- .../src/commands/connect/mod.rs | 10 +- .../src/commands/connecttest.rs | 118 ++++++++++++++++++ .../sparse-05-client/src/commands/mod.rs | 1 + sparse-05/sparse-05-client/src/main.rs | 1 + sparse-05/sparse-05-client/src/options.rs | 7 ++ 5 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 sparse-05/sparse-05-client/src/commands/connecttest.rs diff --git a/sparse-05/sparse-05-client/src/commands/connect/mod.rs b/sparse-05/sparse-05-client/src/commands/connect/mod.rs index 61962aa..c9ce847 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/mod.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/mod.rs @@ -18,11 +18,11 @@ mod commands; mod shell; pub struct Connection { - config: ClientConfig, - foreign_sign_pubkey: ed25519_dalek::PublicKey, - foreign_enc_pubkey: ecies_ed25519::PublicKey, - socket: Arc, - ip: SocketAddr, + pub(crate) config: ClientConfig, + pub(crate) foreign_sign_pubkey: ed25519_dalek::PublicKey, + pub(crate) foreign_enc_pubkey: ecies_ed25519::PublicKey, + pub(crate) socket: Arc, + pub(crate) ip: SocketAddr, } impl Connection { diff --git a/sparse-05/sparse-05-client/src/commands/connecttest.rs b/sparse-05/sparse-05-client/src/commands/connecttest.rs new file mode 100644 index 0000000..0f33c46 --- /dev/null +++ b/sparse-05/sparse-05-client/src/commands/connecttest.rs @@ -0,0 +1,118 @@ +use std::{ + io::{self, Write}, + net::SocketAddr, + path::PathBuf, + sync::Arc, +}; + +use anyhow::bail; +use ed25519_dalek::{Signature, Signer, Verifier}; +use sparse_05_common::messages::{ + Capabilities, Command, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE, +}; +use tokio::{fs, net::UdpSocket}; + +use crate::configs::ClientConfig; + +pub async fn connecttest(config: PathBuf, ip: SocketAddr) -> anyhow::Result<()> { + let config = fs::read(&config).await?; + let config: ClientConfig = rmp_serde::from_slice(&config)?; + + let remote = Arc::new(UdpSocket::bind("0.0.0.0:0").await?); + + let connect_signature = config.keypair.sign(CONNECT_MESSAGE).to_bytes(); + let connect_msg = &[&connect_signature, CONNECT_MESSAGE].concat(); + + let mut counter = 0; + + let (foreign_sign_pubkey, foreign_enc_pubkey, capabilities) = loop { + if counter > 25 { + bail!("could not connect to server"); + } + + print!( + "\rConnecting{}", + match counter % 3 { + 2 => ".. ", + 1 => ". ", + _ => "...", + } + ); + io::stdout().flush().unwrap(); + + remote.send_to(connect_msg, ip).await?; + + let mut buf = [0u8; 2000]; + + match tokio::time::timeout( + std::time::Duration::from_millis(250), + remote.recv_from(&mut buf), + ) + .await + { + Ok(packet) => { + let (count, addr) = packet?; + + if addr != ip { + bail!("received response from wrong source"); + } + + let data = &buf[..count]; + + if data.len() < 65 { + bail!("could not get signature from server"); + } + + let signature: [u8; 64] = data[..64].try_into().unwrap(); + let data = &data[64..]; + + let Ok(foreign_keys) = ecies_ed25519::decrypt(&config.enc_privkey, data) else { + counter += 1; + continue; + }; + + if foreign_keys.len() + < ed25519_dalek::PUBLIC_KEY_LENGTH + ecies_ed25519::PUBLIC_KEY_LENGTH + { + bail!("could not get public keys from the server"); + } + + let foreign_sign_pubkey = ed25519_dalek::PublicKey::from_bytes( + &foreign_keys[..ed25519_dalek::PUBLIC_KEY_LENGTH], + )?; + let foreign_enc_pubkey = ecies_ed25519::PublicKey::from_bytes( + &foreign_keys[ed25519_dalek::PUBLIC_KEY_LENGTH + ..(ed25519_dalek::PUBLIC_KEY_LENGTH + ecies_ed25519::PUBLIC_KEY_LENGTH)], + )?; + + let capabilities: Capabilities = rmp_serde::from_slice( + &foreign_keys + [(ed25519_dalek::PUBLIC_KEY_LENGTH + ecies_ed25519::PUBLIC_KEY_LENGTH)..], + )?; + + foreign_sign_pubkey.verify(data, &Signature::from(signature))?; + + break (foreign_sign_pubkey, foreign_enc_pubkey, capabilities); + } + Err(_) => { + counter += 1; + } + } + }; + + let connection = Arc::new(crate::commands::connect::Connection { + config, + foreign_enc_pubkey, + foreign_sign_pubkey, + socket: Arc::clone(&remote), + ip, + }); + + remote + .send_to(&connection.encrypt_and_sign(CONNECTED_MESSAGE)?, ip) + .await?; + + println!("\rConnected! "); + + Ok(()) +} diff --git a/sparse-05/sparse-05-client/src/commands/mod.rs b/sparse-05/sparse-05-client/src/commands/mod.rs index ef06e7d..7c0a44f 100644 --- a/sparse-05/sparse-05-client/src/commands/mod.rs +++ b/sparse-05/sparse-05-client/src/commands/mod.rs @@ -1,2 +1,3 @@ pub mod connect; +pub mod connecttest; pub mod generate; diff --git a/sparse-05/sparse-05-client/src/main.rs b/sparse-05/sparse-05-client/src/main.rs index 361954d..c7aa9b2 100644 --- a/sparse-05/sparse-05-client/src/main.rs +++ b/sparse-05/sparse-05-client/src/main.rs @@ -15,5 +15,6 @@ async fn main() -> anyhow::Result<()> { commands::generate::generate(name, port, target).await } Command::Connect { config, ip } => commands::connect::connect(config, ip).await, + Command::ConnectTest { config, ip } => commands::connecttest::connecttest(config, ip).await, } } diff --git a/sparse-05/sparse-05-client/src/options.rs b/sparse-05/sparse-05-client/src/options.rs index 2ca922d..d9db116 100644 --- a/sparse-05/sparse-05-client/src/options.rs +++ b/sparse-05/sparse-05-client/src/options.rs @@ -46,6 +46,13 @@ pub enum Command { #[structopt(parse(from_os_str))] config: PathBuf, + #[structopt(parse(try_from_str = to_socket_addr))] + ip: SocketAddr, + }, + ConnectTest { + #[structopt(parse(from_os_str))] + config: PathBuf, + #[structopt(parse(try_from_str = to_socket_addr))] ip: SocketAddr, },