Compare commits
2 Commits
6a2c1ec15d
...
cb22c94cde
Author | SHA1 | Date | |
---|---|---|---|
cb22c94cde | |||
41b33e2844 |
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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::*;
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 => {
|
||||
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
Loading…
Reference in New Issue
Block a user