From 6a5df9054c07874ad071d715be7b29db99756a7c Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sat, 24 Oct 2026 20:11:53 -0600 Subject: [PATCH] add support for session encryption --- Cargo.lock | 52 +++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 24 +++++++++++-- tap_core/Cargo.toml | 2 ++ tap_core/src/audio_source_thread.rs | 8 +++-- tap_core/src/tx_thread.rs | 32 ++++++++++++++++-- tap_gui/Cargo.toml | 1 + tap_gui/src/main.rs | 32 +++++++++++++++--- 8 files changed, 140 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b97605..06033eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,30 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faac5829c2b74c28f830747e7818ccfb684261b5f48a1118b1e2a13d36dfab13" +dependencies = [ + "aws-lc-sys", + "untrusted", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1622d8446a2d4b2ce0c7eefc73dd43a99779028d5ee5c2dd8073a658ba8a2bc" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "bit-set" version = "0.10.0" @@ -988,6 +1012,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ecolor" version = "0.36.2" @@ -1374,6 +1404,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.34" @@ -3527,6 +3563,7 @@ dependencies = [ "clap", "cpal", "env_logger", + "hex", "log", "tap_core", ] @@ -3546,9 +3583,11 @@ dependencies = [ name = "tap_core" version = "0.1.0" dependencies = [ + "aws-lc-rs", "chrono", "circular-buffer", "cpal", + "hex", "log", "opus", "opusic-sys", @@ -3565,6 +3604,7 @@ dependencies = [ "eframe", "egui", "env_logger", + "hex", "log", "tap_core", "thiserror 2.0.21", @@ -3802,6 +3842,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.5.8" @@ -4947,6 +4993,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 5e1a419..366b6a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ log = "0.4.27" env_logger = "0.11.8" tap_core = {path = "tap_core"} cpal = "0.18.2" +hex = "0.4.3" [workspace] members = ["tap_core", "tap_gui"] diff --git a/src/main.rs b/src/main.rs index eba0901..cc60bfa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,8 @@ enum Tasks { src: SocketAddr, /// UDP socket addr to connect to dest: SocketAddr, + /// Session key to use + session_key: String, }, } @@ -36,7 +38,12 @@ fn main() { let host = cpal::default_host(); match opt.command { - Tasks::Tap { device, src, dest } => { + Tasks::Tap { + device, + src, + dest, + session_key, + } => { let udp_connection = match OpusUdpConnection::new(src, dest) { Ok(c) => c, Err(err) => { @@ -49,7 +56,20 @@ fn main() { let audio_source_thread = AudioSourceThread::new(device, host, receiver); - if let Err(err) = audio_source_thread.worker(udp_connection) { + let key = match hex::decode(session_key) { + Ok(key) => key, + Err(err) => { + error!("Failed to parse key: {err}"); + return; + } + }; + + if key.len() != 32 { + error!("Invalid session key length"); + return; + } + + if let Err(err) = audio_source_thread.worker(udp_connection, &key) { error!("Error running tap: {err}"); } } diff --git a/tap_core/Cargo.toml b/tap_core/Cargo.toml index af8463b..ac38ecb 100644 --- a/tap_core/Cargo.toml +++ b/tap_core/Cargo.toml @@ -13,3 +13,5 @@ tap-interface = {version = "0.1.0", registry = "ahines"} thiserror = "2.0.21" opus= { version="0.4.0" } opusic-sys = { version = "0.7.5", features = ["bundled", "no-stack-protector", "no-hardening", "no-fortify-source"] } +aws-lc-rs = "1.18.2" +hex = "0.4.3" diff --git a/tap_core/src/audio_source_thread.rs b/tap_core/src/audio_source_thread.rs index c7613b4..25d3355 100644 --- a/tap_core/src/audio_source_thread.rs +++ b/tap_core/src/audio_source_thread.rs @@ -105,10 +105,14 @@ impl AudioSourceThread { Ok(stream) } - pub fn worker(mut self, connection: T) -> Result<(), Error> { + pub fn worker( + mut self, + connection: T, + key: &[u8], + ) -> Result<(), Error> { let encoder = Self::setup_encoder()?; let (sender, receiver) = channel(); - let tx_thread = TxThread::new(receiver, encoder, connection); + let tx_thread = TxThread::new(receiver, encoder, connection, key); let thread = thread::spawn(move || tx_thread.thread()); let mut stream = self.setup_stream(&sender)?; diff --git a/tap_core/src/tx_thread.rs b/tap_core/src/tx_thread.rs index 673ed31..f276531 100644 --- a/tap_core/src/tx_thread.rs +++ b/tap_core/src/tx_thread.rs @@ -1,5 +1,7 @@ use crate::util::STEREO_FRAME_SIZE; use crate::util::{CIRC_BUFFER_SIZE, TAP_PACKET_MAX_DATA_LEN, ThreadMessage}; +use aws_lc_rs::aead::{AES_256_GCM, Aad, RandomizedNonceKey}; +use aws_lc_rs::error::Unspecified; use chrono::Utc; use circular_buffer::FixedCircularBuffer; use log::{error, info, warn}; @@ -18,6 +20,8 @@ pub enum Error { IoFailure(#[from] std::io::Error), #[error("Encode error: {0}")] EncodeFailure(#[from] opus::Error), + #[error("Failed to encrypt data")] + EncryptionFailed(#[from] Unspecified), } pub struct TxThread { @@ -27,10 +31,16 @@ pub struct TxThread { circular_buffer: FixedCircularBuffer, seq_count: u32, exit: bool, + key: RandomizedNonceKey, } impl TxThread { - pub fn new(receiver: Receiver, encoder: Encoder, opus_connection: T) -> Self { + pub fn new( + receiver: Receiver, + encoder: Encoder, + opus_connection: T, + key: &[u8], + ) -> Self { Self { receiver, encoder, @@ -38,6 +48,7 @@ impl TxThread { circular_buffer: Default::default(), seq_count: 0, exit: false, + key: RandomizedNonceKey::new(&AES_256_GCM, key).unwrap(), } } @@ -46,6 +57,18 @@ impl TxThread { Ok(self.encoder.encode_float(&encode_buffer, output_buffer)?) } + fn encrypt_data(&self, frame: &[u8]) -> Result, Error> { + let mut enc_data = frame.to_vec(); + let nonce = self + .key + .seal_in_place_append_tag(Aad::empty(), &mut enc_data)?; + + let mut msg = nonce.as_ref().to_vec(); + msg.append(&mut enc_data); + + Ok(msg) + } + fn send_opus_frame(&mut self) -> Result<(), Error> { let mut output_buffer = vec![0; TAP_PACKET_MAX_DATA_LEN]; let size = self.encode(&mut output_buffer)?; @@ -58,8 +81,11 @@ impl TxThread { audio_data: output_buffer, }; - self.opus_connection - .write_all(tap_message.encode_to_vec().as_slice())?; + let tap_message = tap_message.encode_to_vec(); + + let enc_message = self.encrypt_data(&tap_message)?; + + self.opus_connection.write_all(enc_message.as_slice())?; (self.seq_count, _) = self.seq_count.overflowing_add(1); Ok(()) diff --git a/tap_gui/Cargo.toml b/tap_gui/Cargo.toml index 0fb4537..4d5ac63 100644 --- a/tap_gui/Cargo.toml +++ b/tap_gui/Cargo.toml @@ -12,3 +12,4 @@ egui = "0.36.2" eframe = "0.36.2" tokio = { version = "1.53.2", features = ["rt", "rt-multi-thread", "macros"] } thiserror = "2.0.21" +hex = "0.4.3" diff --git a/tap_gui/src/main.rs b/tap_gui/src/main.rs index 840ce9d..3be3ba1 100644 --- a/tap_gui/src/main.rs +++ b/tap_gui/src/main.rs @@ -31,6 +31,8 @@ pub struct Tap { device: String, denny_jack_host: String, warning: String, + key: String, + decoded_key: Vec, join_handle: Option>>, channel: Option>, host: cpal::Host, @@ -49,6 +51,8 @@ impl Tap { device: "".to_string(), denny_jack_host: "158.69.121.46:9999".to_string(), warning: "".to_string(), + key: "".to_string(), + decoded_key: vec![], join_handle: None, channel: None, host: cpal::default_host(), @@ -58,6 +62,7 @@ impl Tap { pub fn bg_threads( device: &str, host: &str, + key: &[u8], channel: Receiver, ) -> Result<(), ThreadError> { let udp_connection = @@ -72,7 +77,7 @@ impl Tap { let audio_source_thread = AudioSourceThread::new(device.to_string(), host, channel); // Run until completion - audio_source_thread.worker(udp_connection)?; + audio_source_thread.worker(udp_connection, key)?; Ok(()) } @@ -89,6 +94,9 @@ impl App for Tap { State::Init => { ui.label("Denny Jack Host"); ui.text_edit_singleline(&mut self.denny_jack_host); + ui.label("Denny Jack Session Key"); + ui.text_edit_singleline(&mut self.key); + egui::containers::ComboBox::from_label("Device") .selected_text(&self.device) .show_ui(ui, |ui| { @@ -102,11 +110,21 @@ impl App for Tap { }); if ui.button("Connect").clicked() { - if self.device.is_empty() || self.denny_jack_host.is_empty() { + if self.device.is_empty() + || self.denny_jack_host.is_empty() + || self.key.is_empty() + { self.warning = "Enter connection details and try again".to_string(); } else { - self.warning = "".to_string(); - self.mode = State::Connecting; + if let Ok(key) = hex::decode(&self.key) + && key.len() == 32 + { + self.warning = "".to_string(); + self.mode = State::Connecting; + self.decoded_key = key; + } else { + self.warning = "Invalid session key".to_string() + } } } @@ -121,7 +139,10 @@ impl App for Tap { let (sender, receiver) = channel(); - let join = thread::spawn(move || Self::bg_threads(&device, &host, receiver)); + let decoded_key = self.decoded_key.clone(); + let join = thread::spawn(move || { + Self::bg_threads(&device, &host, &decoded_key, receiver) + }); self.join_handle = Some(join); self.channel = Some(sender); @@ -148,6 +169,7 @@ impl App for Tap { let _ = handle.join(); self.join_handle = None; self.mode = State::Init; + self.decoded_key = vec![]; } } }