Skip to content

Commit

Permalink
working key handshake, client subcommands, just need to start encrypt…
Browse files Browse the repository at this point in the history
…ing and sending content
  • Loading branch information
Sarsoo committed Feb 11, 2024
1 parent 6e954a9 commit 6cef749
Show file tree
Hide file tree
Showing 22 changed files with 667 additions and 187 deletions.
6 changes: 6 additions & 0 deletions dnstp-client/src/download.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use crate::NetSettings;

pub fn download(net_settings: NetSettings)
{

}
99 changes: 50 additions & 49 deletions dnstp-client/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,58 @@
//! # Client Side
//!

mod test;
mod upload;
mod download;

use std::fs::OpenOptions;
use std::net::SocketAddr;
use std::thread;
use std::time::Duration;
use clap::Parser;
use log::{info, LevelFilter};
use rand::RngCore;
use clap::{Parser, Subcommand};
use log::{LevelFilter};
use simplelog::*;
use dnstplib::DomainConfig;

use dnstplib::message::DNSMessage;
use dnstplib::net::{DNSSocket, NetworkMessage};
use dnstplib::processor::ResponseProcesor;
use crate::download::download;
use crate::test::send_test_requests;
use crate::upload::upload;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Addresses to send requests
#[clap(subcommand)]
command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
/// Send test requests on loop to the server
Test {
#[clap(flatten)]
net_options: NetSettings
},
/// Upload data to the remote server
Upload {
#[clap(flatten)]
net_options: NetSettings,
#[arg(short, long)]
value: String
},
/// Download a payload from the remote server
Download {
#[clap(flatten)]
net_options: NetSettings
}
}

#[derive(Parser, Debug)]
struct NetSettings {
/// Server address to send requests to
#[arg(short, long)]
address: String,
/// Base domain to operate on
/// Base domain server is operating on
#[arg(long)]
base_domain: String,
/// Sub-domain to handle key handling when requested
#[arg(long, default_value = "static")]
key_endpoint: String
#[arg(short, long, default_value = "static")]
key_endpoint: String,
}

fn main() {
Expand All @@ -44,40 +70,15 @@ fn main() {

let args = Args::parse();

let address = SocketAddr::from(([127, 0, 0, 1], 0));

let mut socket = DNSSocket::new(vec!(address));
socket.bind();
socket.run_tx();

let tx_channel = socket.get_tx_message_channel().unwrap();

let mut processor = ResponseProcesor::new();
processor.run();

socket.run_rx(processor.get_message_channel().expect("couldn't get message processing channel"));

let domain_config = DomainConfig {
base_domain: args.base_domain,
key_endpoint: args.key_endpoint
};

let domain = domain_config.get_fq_key_endpoint();

let mut rng = rand::thread_rng();
loop {

info!("sending...");

let message = DNSMessage::req_from_hostname(address, rng.next_u32() as u16, domain.clone());

let bytes = message.to_bytes();

tx_channel.send(Box::new(NetworkMessage {
buffer: Box::new(bytes),
peer: args.address.parse().unwrap()
}));

thread::sleep(Duration::from_secs(1));
match args.command {
Command::Test { net_options } => {
send_test_requests(net_options);
}
Command::Upload { net_options, value } => {
upload(net_options, value);
}
Command::Download { net_options } => {
download(net_options);
}
}
}
52 changes: 52 additions & 0 deletions dnstp-client/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use log::info;
use rand::RngCore;
use dnstplib::client_crypto_context::ClientCryptoContext;
use dnstplib::DomainConfig;
use dnstplib::message::DNSMessage;
use dnstplib::net::{DNSSocket, NetworkMessage};
use dnstplib::processor::ResponseProcesor;
use crate::NetSettings;

pub fn send_test_requests(args: NetSettings)
{
let address = SocketAddr::from(([127, 0, 0, 1], 0));

let mut socket = DNSSocket::new(vec!(address));
socket.bind();
socket.run_tx();

let tx_channel = socket.get_tx_message_channel().unwrap();

let crypto_context = Arc::new(Mutex::new(ClientCryptoContext::new()));
let mut processor = ResponseProcesor::new(crypto_context.clone());
processor.run();

socket.run_rx(processor.get_message_channel().expect("couldn't get message processing channel"));

let domain_config = DomainConfig {
base_domain: args.base_domain,
key_endpoint: args.key_endpoint
};

let domain = domain_config.get_fq_key_endpoint();

let mut rng = rand::thread_rng();
loop {
info!("sending...");

let message = DNSMessage::req_from_hostname(address, rng.next_u32() as u16, domain.clone());

let bytes = message.to_bytes();

tx_channel.send(Box::new(NetworkMessage {
buffer: Box::new(bytes),
peer: args.address.parse().unwrap()
}));

thread::sleep(Duration::from_secs(1));
}
}
81 changes: 81 additions & 0 deletions dnstp-client/src/upload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use log::info;
use rand::RngCore;
use rand::rngs::OsRng;
use dnstplib::client_crypto_context::ClientCryptoContext;
use dnstplib::{DomainConfig, send_message};
use dnstplib::message::{Direction, DNSHeader, DNSMessage, DNSQuestion, Opcode, QClass, QType, ResponseCode};
use dnstplib::net::DNSSocket;
use dnstplib::processor::ResponseProcesor;
use crate::NetSettings;

pub fn upload(net_settings: NetSettings, value: String)

Check warning on line 15 in dnstp-client/src/upload.rs

View workflow job for this annotation

GitHub Actions / Build & Test

unused variable: `value`
{
let address = SocketAddr::from(([127, 0, 0, 1], 0));

let mut socket = DNSSocket::new(vec!(address));
socket.bind();
socket.run_tx();

let tx_channel = socket.get_tx_message_channel().unwrap();

let crypto_context = Arc::new(Mutex::new(ClientCryptoContext::new()));
let mut processor = ResponseProcesor::new(crypto_context.clone());
processor.run();

socket.run_rx(processor.get_message_channel().expect("couldn't get message processing channel"));

let domain_config = DomainConfig {
base_domain: net_settings.base_domain,
key_endpoint: net_settings.key_endpoint
};

info!("sending handshake...");

let message = DNSMessage {
header: DNSHeader {
id: OsRng.next_u32() as u16,
direction: Direction::Request,
opcode: Opcode::Query,
authoritative: false,
truncation: false,
recursion_desired: false,
recursion_available: false,
valid_zeroes: true,
response: ResponseCode::NoError,
question_count: 2,
answer_record_count: 0,
authority_record_count: 0,
additional_record_count: 0,
},
questions: vec![
DNSQuestion {
qname: domain_config.get_fq_key_endpoint(),
qtype: QType::A,
qclass: QClass::Internet,
},
DNSQuestion {
qname: crypto_context.lock().unwrap().get_public_key_domain(&domain_config.base_domain),
qtype: QType::A,
qclass: QClass::Internet,
}
],
answer_records: vec![],
authority_records: vec![],
additional_records: vec![],
peer: net_settings.address.parse().unwrap(),
};

send_message(message, &tx_channel);

while !crypto_context.lock().unwrap().is_complete() {
info!("waiting for crypto completion...");

thread::sleep(Duration::from_millis(100));
}

info!("crypto complete, sending data");
}
39 changes: 39 additions & 0 deletions dnstp/src/client_crypto_context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use aes_gcm_siv::Aes256GcmSiv;
use p256::ecdh::EphemeralSecret;
use crate::crypto::{get_random_asym_pair, trim_public_key};
use crate::string::append_base_domain_to_key;

/// Represents the server from the perspective of a client
pub struct ClientCryptoContext {
pub shared_key: Option<Aes256GcmSiv>,
pub client_private: EphemeralSecret,
pub client_public: String,
pub server_public: Option<String>
}

impl ClientCryptoContext {
pub fn new() -> Self {
let (client_private, client_public) = get_random_asym_pair();

Self {
shared_key: None,
client_private,
client_public,
server_public: None
}
}

pub fn is_complete(&self) -> bool
{
self.server_public.is_some() && self.shared_key.is_some()
}

pub fn get_public_key_domain(&self, base_domain: &String) -> String
{
append_base_domain_to_key(
trim_public_key(&self.client_public),
base_domain
)
}
}

35 changes: 34 additions & 1 deletion dnstp/src/clients.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
//! Structures for managing the state of connected clients from the perspective of the server

use std::collections::HashMap;
use std::time::SystemTime;
use aes_gcm_siv::Aes256GcmSiv;

/// A single client including when they connected and their shared cryptographic key
pub struct Client {
pub first_seen: SystemTime,
pub last_seen: SystemTime,
pub shared_key: Aes256GcmSiv
}

impl Client {

/// Create a new client as
pub fn new(shared_key: Aes256GcmSiv) -> Client
{
let time = SystemTime::now();

Client {
first_seen: SystemTime::now(),
first_seen: time,
last_seen: time,
shared_key
}
}

pub fn bump_last_seen(&mut self)
{
self.last_seen = SystemTime::now();
}
}

/// Container for managing connected clients and their keys
pub struct Clients {
client_map: HashMap<String, Client>
}

impl Clients {

/// Create a new collection of clients
pub fn new() -> Clients
{
Clients {
Expand All @@ -36,8 +51,26 @@ impl Clients {
// self.client_map.insert(client_id, Client::new(shared_key));
// }

/// Add a newly connected client to the collection of connections. Index the client by public key.
pub fn add(&mut self, client_id: String, client:Client)
{
self.client_map.insert(client_id, client);
}

pub fn client_is_connected(&self, client_id: &String) -> bool
{
self.client_map.contains_key(client_id)
}

pub fn bump_last_seen(&mut self, client_id: &String) -> Result<(), ()>
{
match self.client_map.get_mut(client_id)
{
None => Err(()),
Some(client) => {
client.bump_last_seen();
Ok(())
}
}
}
}
4 changes: 2 additions & 2 deletions dnstp/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ pub fn get_random_asym_pair() -> (EphemeralSecret, String)
}

/// Use one private key and an opposing public key to arrive at the same shared secret
pub fn get_shared_asym_secret(secret: EphemeralSecret, opposing_public_key: String) -> Result<SharedSecret<NistP256>, ()> {
pub fn get_shared_asym_secret(secret: &EphemeralSecret, opposing_public_key: &String) -> Result<SharedSecret<NistP256>, ()> {

match PublicKey::from_str(&opposing_public_key) {
match PublicKey::from_str(opposing_public_key) {
Ok(other_public) => {
Ok(secret.diffie_hellman(&other_public))
}
Expand Down
Loading

0 comments on commit 6cef749

Please sign in to comment.