Compare commits

...

2 Commits

Author SHA1 Message Date
cb22c94cde
bumping version number
All checks were successful
Build Binaries / Build & Test (push) Successful in 36s
Build Binaries / Package Library (push) Successful in 39s
Build Binaries / Package Container (push) Successful in 1m30s
2024-07-29 21:51:30 +01:00
41b33e2844
adding key value upload capability 2024-07-29 21:51:05 +01:00
12 changed files with 247 additions and 61 deletions

6
Cargo.lock generated
View File

@ -257,7 +257,7 @@ dependencies = [
[[package]]
name = "dnstp"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"clap",
"dnstplib",
@ -267,7 +267,7 @@ dependencies = [
[[package]]
name = "dnstp-client"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"base64",
"clap",
@ -279,7 +279,7 @@ dependencies = [
[[package]]
name = "dnstplib"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"aes-gcm-siv",
"base64",

View File

@ -8,6 +8,6 @@ members = [
]
[workspace.package]
version = "0.1.1"
version = "0.1.2"
authors = ["sarsoo <andy@sarsoo.xyz>"]
documentation = "https://sarsoo.github.io/dnstp"

View File

@ -34,6 +34,8 @@ enum Command {
#[clap(flatten)]
net_options: NetSettings,
#[arg(short, long)]
key: Option<Vec<String>>,
#[arg(short, long)]
value: Vec<String>
},
/// Download a payload from the remote server
@ -52,7 +54,7 @@ struct NetSettings {
#[arg(long)]
base_domain: String,
/// Sub-domain to handle key handling when requested
#[arg(short, long, default_value = "static")]
#[arg(long, default_value = "static")]
key_endpoint: String,
}
@ -60,7 +62,7 @@ fn main() {
CombinedLogger::init(
vec![
TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
WriteLogger::new(LevelFilter::Info, Config::default(), OpenOptions::new()
WriteLogger::new(LevelFilter::Trace, Config::default(), OpenOptions::new()
.read(true)
.write(true)
.append(true)
@ -75,8 +77,8 @@ fn main() {
Command::Test { net_options } => {
send_test_requests(net_options);
}
Command::Upload { net_options, value } => {
upload(net_options, value);
Command::Upload { net_options, key, value } => {
upload(net_options, key, value);
}
Command::Download { net_options } => {
download(net_options);

View File

@ -4,14 +4,21 @@ use std::thread;
use std::time::Duration;
use log::info;
use rand::rngs::OsRng;
use dnstplib::session::{ClientCryptoContext, generate_client_handshake_message, generate_string_encryption_message};
use dnstplib::session::{ClientCryptoContext, generate_client_handshake_message, generate_key_string_encryption_message, generate_string_encryption_message};
use dnstplib::{DomainConfig, send_message};
use dnstplib::net::DNSSocket;
use dnstplib::processor::ResponseProcesor;
use crate::NetSettings;
pub fn upload(net_settings: NetSettings, values: Vec<String>)
pub fn upload(net_settings: NetSettings, keys: Option<Vec<String>>, values: Vec<String>)
{
if let Some(keys) = &keys {
if keys.len() > values.len() {
println!("Cannot provide more keys than values [{} keys] and [{} values]", keys.len(), values.len());
return;
}
}
let address = SocketAddr::from(([127, 0, 0, 1], 0));
let mut socket = DNSSocket::new(vec!(address));
@ -45,6 +52,9 @@ pub fn upload(net_settings: NetSettings, values: Vec<String>)
info!("crypto complete, sending data");
match keys {
// no keys, just upload values
None => {
for v in values {
info!("sending [{}]", v);
@ -60,3 +70,45 @@ pub fn upload(net_settings: NetSettings, values: Vec<String>)
}
}
}
// keys, present loop through keys to send associated values, then send any un-keyed values
Some(keys) => {
let mut k_index = 0;
for k in keys {
let v = values.get(k_index).unwrap().clone();
info!("sending [{}]:[{}]", k, v);
if let Ok(encryption_message) = generate_key_string_encryption_message(
k,
v,
&mut OsRng,
&domain_config,
crypto_context.clone(),
&net_settings.address
) {
send_message(encryption_message, &tx_channel);
}
k_index = k_index + 1;
}
if k_index < values.len() {
for v in &values[k_index..] {
info!("sending [{}]", v);
if let Ok(encryption_message) = generate_string_encryption_message(
v.clone(),
&mut OsRng,
&domain_config,
crypto_context.clone(),
&net_settings.address
) {
send_message(encryption_message, &tx_channel);
}
}
}
}
}
}

View File

@ -35,7 +35,7 @@ fn main() {
CombinedLogger::init(
vec![
TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
WriteLogger::new(LevelFilter::Info, Config::default(), OpenOptions::new()
WriteLogger::new(LevelFilter::Trace, Config::default(), OpenOptions::new()
.read(true)
.write(true)
.append(true)

View File

@ -2,6 +2,8 @@ use std::net::{Ipv4Addr, SocketAddr};
use crate::message::{DNSQuestion, DNSHeader, questions_to_bytes, Direction, ResponseCode, QType, QClass, ResourceRecord, records_to_bytes, ARdata, TXTRdata, RData};
use crate::RequestError;
pub const MESSAGE_SIZE: usize = 512;
/// A DNS message which can be used as either a request or response based on its direction and composition
#[derive(Debug)]
pub struct DNSMessage {

View File

@ -8,5 +8,5 @@ pub mod message_parser;
pub use question::{DNSQuestion, QClass, QType, QuestionParseError, questions_from_bytes, questions_to_bytes};
pub use record::{AAAARdata, ARdata, RawRData, RData, RecordParseError, records_from_bytes, records_to_bytes, ResourceRecord, TXTRdata};
pub use header::{Direction, DNSHeader, HEADER_SIZE, Opcode, ResponseCode};
pub use message::DNSMessage;
pub use message::{DNSMessage, MESSAGE_SIZE};
pub use message_parser::*;

View File

@ -1,11 +1,11 @@
use std::net::{SocketAddr, UdpSocket};
use std::thread;
use std::thread::{JoinHandle};
use log::{debug, error, info};
use log::{debug, error, info, warn};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use crate::message::HEADER_SIZE;
use crate::message::{HEADER_SIZE, MESSAGE_SIZE};
use crate::net::{NetworkMessage, NetworkMessagePtr};
pub struct DNSSocket {
@ -76,8 +76,8 @@ impl DNSSocket {
Some(s) => {
let mut cancelled = false;
while !cancelled {
let mut buf = Box::new(Vec::with_capacity(512));
buf.resize(512, 0);
let mut buf = Box::new(Vec::with_capacity(MESSAGE_SIZE));
buf.resize(MESSAGE_SIZE, 0);
let res = s.recv_from(&mut (*buf));
match res {
@ -92,7 +92,7 @@ impl DNSSocket {
}
}
else {
debug!("skipping processing message from [{}], message isn't longer than standard header", peer);
debug!("[{}] skipping processing message, message isn't longer than standard header", peer);
}
}
Err(_) => {}
@ -131,8 +131,14 @@ impl DNSSocket {
while !cancelled {
for m in &msg_rx {
let message_length = m.buffer.len();
if message_length > MESSAGE_SIZE {
warn!("[{}] message is longer than standard maximum [{} bytes]", m.peer, message_length);
}
if let Err(e) = s.send_to(&(*m.buffer), m.peer){
error!("error sending response to [{}], {}", m.peer, e);
error!("[{}] error sending response {}", m.peer, e);
}
}

View File

@ -95,7 +95,7 @@ impl RequestProcesor {
info!("[{}] received request from known client", peer);
// for now lets deal with three questions, first one is the client id, second is the actual request, third is the nonce
if r.questions.len() == 3
if r.questions.len() == 3 || r.questions.len() == 4
{
match r.questions[1].qtype {
QType::A => {

View File

@ -1,7 +1,10 @@
use std::net::SocketAddr;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use aes_gcm_siv::aead::consts::U12;
use aes_gcm_siv::aead::generic_array::GenericArray;
use aes_gcm_siv::Nonce;
use base64::DecodeError;
use log::{info, error};
use base64::prelude::*;
@ -14,6 +17,54 @@ use crate::message::DNSMessage;
use crate::net::NetworkMessagePtr;
use crate::processor::RequestProcesor;
struct UploadValue {
pub key: Option<String>,
pub value: String,
pub nonce: String
}
impl UploadValue {
pub fn from(r: &DNSMessage) -> Option<UploadValue> {
if r.questions.len() == 3 {
return Some(UploadValue {
key: None,
value: r.questions[1].qname.clone(),
nonce: r.questions[2].qname.clone()
});
}
else if r.questions.len() == 4 {
return Some(UploadValue {
key: Some(r.questions[1].qname.clone()),
value: r.questions[2].qname.clone(),
nonce: r.questions[3].qname.clone()
});
}
None
}
pub fn get_decoded_nonce(&self) -> Result<Vec<u8>, DecodeError>
{
BASE64_STANDARD.decode(&self.nonce)
}
pub fn get_decoded_encrypted_key(&self) -> Option<Vec<u8>>
{
if let Some(key) = &self.key {
if let Ok(decode) = BASE64_STANDARD.decode(key) {
return Some(decode);
}
}
None
}
pub fn get_decoded_encrypted_value(&self) -> Result<Vec<u8>, DecodeError>
{
BASE64_STANDARD.decode(&self.value)
}
}
impl RequestProcesor {
pub fn handle_upload_request(r: DNSMessage, _sending_channel: &Sender<NetworkMessagePtr>, clients: &Arc<Mutex<Clients>>, peer: SocketAddr)
{
@ -24,31 +75,30 @@ impl RequestProcesor {
error!("[{}] failed to bump last seen time", peer);
}
let encrypted_value = BASE64_STANDARD.decode(r.questions[1].qname.clone());
let nonce_value = BASE64_STANDARD.decode(r.questions[2].qname.clone());
if let Some(value_context) = UploadValue::from(&r) {
match (encrypted_value, nonce_value) {
match (value_context.get_decoded_encrypted_value(), value_context.get_decoded_nonce()) {
(Ok(encrypted_value), Ok(nonce_value)) => {
let nonce = Nonce::from_slice(nonce_value.as_slice());
let decrypted = decrypt(clients.lock().unwrap().get_shared_key(client_id).unwrap(), nonce, &encrypted_value).unwrap();
let mut clients = clients.lock().unwrap();
let shared_key = clients.get_shared_key(client_id).unwrap();
let decrypted = decrypt(shared_key, nonce, &encrypted_value).unwrap();
let decrypted_string = String::from_utf8(decrypted).unwrap();
info!("[{}] decrypted [{}] from peer", peer, decrypted_string.as_str());
match value_context.get_decoded_encrypted_key() {
Some(encrypted_key) => {
let decrypted_key = decrypt(shared_key, nonce, &encrypted_key).unwrap();
let decrypted_key_string = String::from_utf8(decrypted_key).unwrap();
info!("[{}] decrypted [{}]:[{}] from peer", peer, decrypted_key_string.as_str(), decrypted_string.as_str());
}
None => {
info!("[{}] decrypted [{}] from peer", peer, decrypted_string.as_str());
}
}
// let mut file = OpenOptions::new()
// .read(true)
// .write(true)
// .append(true)
// .create(true)
// .open(client_id)
// .unwrap();
//
// if let Err(e) = file.write(decrypted_string.as_bytes()) {
// error!("[{}] couldn't write to file: {}", peer, e);
// }
// if let Err(e) = file.write("\n".as_bytes()) {
// error!("[{}] couldn't write to file: {}", peer, e);
// }
}
(Err(e), _) => {
error!("[{}] failed to decode encrypted value from peer: {}", peer, e);
@ -59,3 +109,4 @@ impl RequestProcesor {
}
}
}
}

View File

@ -113,3 +113,76 @@ pub fn get_string_encryption_message(msg_id: u16, public_key_domain: String, enc
peer: peer.parse().unwrap(),
}
}
pub fn generate_key_string_encryption_message(key: String, value: String, rand: &mut OsRng, domain_config: &DomainConfig, crypto_context: Arc<Mutex<ClientCryptoContext>>, peer: &String) -> Result<DNSMessage, ()> {
let nonce = generate_aes_nonce();
let encrypted_key = encrypt(&crypto_context.lock().unwrap().shared_key.clone().unwrap(), &nonce, &key.clone().into_bytes());
let encrypted_value = encrypt(&crypto_context.lock().unwrap().shared_key.clone().unwrap(), &nonce, &value.clone().into_bytes());
match (encrypted_key, encrypted_value) {
(Ok(encrypted_key), Ok(encrypted_value)) => {
let encrypted_key = BASE64_STANDARD.encode(encrypted_key);
let encrypted_value = BASE64_STANDARD.encode(encrypted_value);
let nonce_string = BASE64_STANDARD.encode(nonce);
return Ok(get_key_string_encryption_message(
rand.next_u32() as u16,
crypto_context.lock().unwrap().get_public_key_domain(&domain_config.base_domain),
encrypted_key,
encrypted_value,
nonce_string,
peer
))
}
(_, _) => {}
}
Err(())
}
pub fn get_key_string_encryption_message(msg_id: u16, public_key_domain: String, encrypted_key: String, encrypted_string: String, nonce_string: String, peer: &String) -> DNSMessage {
DNSMessage {
header: DNSHeader {
id: msg_id,
direction: Direction::Request,
opcode: Opcode::Query,
authoritative: false,
truncation: false,
recursion_desired: false,
recursion_available: false,
valid_zeroes: true,
response: ResponseCode::NoError,
question_count: 4,
answer_record_count: 0,
authority_record_count: 0,
additional_record_count: 0,
},
questions: vec![
DNSQuestion {
qname: public_key_domain,
qtype: QType::A,
qclass: QClass::Internet,
},
DNSQuestion {
qname: encrypted_key,
qtype: QType::A,
qclass: QClass::Internet,
},
DNSQuestion {
qname: encrypted_string,
qtype: QType::A,
qclass: QClass::Internet,
},
DNSQuestion {
qname: nonce_string,
qtype: QType::A,
qclass: QClass::Internet,
}
],
answer_records: vec![],
authority_records: vec![],
additional_records: vec![],
peer: peer.parse().unwrap(),
}
}

View File

@ -4,4 +4,4 @@ mod message_generator;
pub use clients::Clients;
pub use client_crypto_context::ClientCryptoContext;
pub use message_generator::{generate_client_handshake_message, generate_string_encryption_message};
pub use message_generator::{generate_client_handshake_message, generate_string_encryption_message, generate_key_string_encryption_message};