feat: added connection and authentication
client can now generate a server binary, and try to connect to it and get capabilities
This commit is contained in:
@@ -1,17 +1,51 @@
|
||||
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
|
||||
|
||||
use ed25519_dalek::{Keypair, Signer};
|
||||
use sparse_05_common::messages::CONNECT_MESSAGE;
|
||||
use anyhow::{bail, Context};
|
||||
use ed25519_dalek::{Keypair, Signature, Signer, Verifier};
|
||||
use rmp_serde::decode::ReadSlice;
|
||||
use sparse_05_common::messages::{Capabilities, Response, CONNECTED_MESSAGE, CONNECT_MESSAGE};
|
||||
use tokio::{fs, net::UdpSocket};
|
||||
|
||||
use crate::configs::ClientConfig;
|
||||
|
||||
enum State {
|
||||
Authenticating,
|
||||
Ready,
|
||||
UploadingFile,
|
||||
DownloadingFile,
|
||||
}
|
||||
|
||||
struct Connection {
|
||||
config: ClientConfig,
|
||||
foreign_sign_pubkey: ed25519_dalek::PublicKey,
|
||||
foreign_enc_pubkey: ecies_ed25519::PublicKey,
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
fn decrypt_and_verify(&self, data: &[u8]) -> anyhow::Result<Response> {
|
||||
if data.len() < 65 {
|
||||
bail!("unable to parse out signature from message");
|
||||
}
|
||||
|
||||
let signature: [u8; 64] = data[..64].try_into().unwrap();
|
||||
self.foreign_sign_pubkey
|
||||
.verify(data, &Signature::from(signature))?;
|
||||
|
||||
let data = ecies_ed25519::decrypt(&self.config.enc_privkey, &data[64..])?;
|
||||
|
||||
Ok(rmp_serde::from_slice(&data)?)
|
||||
}
|
||||
|
||||
fn encrypt_and_sign(&self, data: &[u8]) -> anyhow::Result<Vec<u8>> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let data = ecies_ed25519::encrypt(&self.foreign_enc_pubkey, data, &mut rng)?;
|
||||
|
||||
let signature = self.config.keypair.sign(&data);
|
||||
|
||||
Ok([&signature.to_bytes(), &*data].concat())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn connect(config: PathBuf, ip: SocketAddr) -> anyhow::Result<()> {
|
||||
let config = fs::read(&config).await?;
|
||||
let config: ClientConfig = rmp_serde::from_slice(&config)?;
|
||||
@@ -21,7 +55,84 @@ pub async fn connect(config: PathBuf, ip: SocketAddr) -> anyhow::Result<()> {
|
||||
let connect_signature = config.keypair.sign(CONNECT_MESSAGE).to_bytes();
|
||||
let connect_msg = &[&connect_signature, CONNECT_MESSAGE].concat();
|
||||
|
||||
remote.send_to(connect_msg, ip).await?;
|
||||
let mut counter = 0;
|
||||
|
||||
let (foreign_sign_pubkey, foreign_enc_pubkey, capabilities) = loop {
|
||||
if counter > 5 {
|
||||
bail!("could not connect to server");
|
||||
}
|
||||
|
||||
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 = Connection {
|
||||
config,
|
||||
foreign_enc_pubkey,
|
||||
foreign_sign_pubkey,
|
||||
};
|
||||
|
||||
remote
|
||||
.send_to(&connection.encrypt_and_sign(CONNECTED_MESSAGE)?, ip)
|
||||
.await?;
|
||||
|
||||
println!("connected! {:?}", capabilities);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ pub const SPARSE_SERVER_BINARY: &'static [u8] =
|
||||
pub async fn generate(mut name: PathBuf, port: u16) -> anyhow::Result<()> {
|
||||
let mut csprng = rand::thread_rng();
|
||||
let keypair = Keypair::generate(&mut csprng);
|
||||
let (enc_privkey, enc_pubkey) = ecies_ed25519::generate_keypair(&mut csprng);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -28,11 +29,17 @@ pub async fn generate(mut name: PathBuf, port: u16) -> anyhow::Result<()> {
|
||||
file.write_all(CONFIG_SEPARATOR).await?;
|
||||
file.write_all(&port.to_be_bytes()[..]).await?;
|
||||
file.write_all(keypair.public.as_bytes()).await?;
|
||||
file.write_all(enc_pubkey.as_bytes()).await?;
|
||||
|
||||
let config = ClientConfig { keypair, port };
|
||||
let config = ClientConfig {
|
||||
keypair,
|
||||
enc_privkey,
|
||||
enc_pubkey,
|
||||
port,
|
||||
};
|
||||
|
||||
let mut file_part = name.file_name().unwrap().to_owned();
|
||||
file_part.push(OsString::from(".conf"));
|
||||
file_part.push(OsString::from(".scon"));
|
||||
name.pop();
|
||||
name.push(file_part);
|
||||
let mut file = fs::OpenOptions::new()
|
||||
|
||||
@@ -4,5 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ClientConfig {
|
||||
pub keypair: Keypair,
|
||||
pub enc_pubkey: ecies_ed25519::PublicKey,
|
||||
pub enc_privkey: ecies_ed25519::SecretKey,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user