add support for session encryption
This commit is contained in:
8 files changed
+138
-10
No files matched your search
Generated
+52
@@ -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"
|
||||
|
||||
@@ -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"]
|
||||
|
||||
+22
-2
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -105,10 +105,14 @@ impl AudioSourceThread {
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
pub fn worker<T: Read + Write + Send + 'static>(mut self, connection: T) -> Result<(), Error> {
|
||||
pub fn worker<T: Read + Write + Send + 'static>(
|
||||
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)?;
|
||||
|
||||
@@ -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<T: Read + Write> {
|
||||
@@ -27,10 +31,16 @@ pub struct TxThread<T: Read + Write> {
|
||||
circular_buffer: FixedCircularBuffer<f32, CIRC_BUFFER_SIZE>,
|
||||
seq_count: u32,
|
||||
exit: bool,
|
||||
key: RandomizedNonceKey,
|
||||
}
|
||||
|
||||
impl<T: Read + Write> TxThread<T> {
|
||||
pub fn new(receiver: Receiver<ThreadMessage>, encoder: Encoder, opus_connection: T) -> Self {
|
||||
pub fn new(
|
||||
receiver: Receiver<ThreadMessage>,
|
||||
encoder: Encoder,
|
||||
opus_connection: T,
|
||||
key: &[u8],
|
||||
) -> Self {
|
||||
Self {
|
||||
receiver,
|
||||
encoder,
|
||||
@@ -38,6 +48,7 @@ impl<T: Read + Write> TxThread<T> {
|
||||
circular_buffer: Default::default(),
|
||||
seq_count: 0,
|
||||
exit: false,
|
||||
key: RandomizedNonceKey::new(&AES_256_GCM, key).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +57,18 @@ impl<T: Read + Write> TxThread<T> {
|
||||
Ok(self.encoder.encode_float(&encode_buffer, output_buffer)?)
|
||||
}
|
||||
|
||||
fn encrypt_data(&self, frame: &[u8]) -> Result<Vec<u8>, 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<T: Read + Write> TxThread<T> {
|
||||
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(())
|
||||
|
||||
@@ -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"
|
||||
+25
-3
@@ -31,6 +31,8 @@ pub struct Tap {
|
||||
device: String,
|
||||
denny_jack_host: String,
|
||||
warning: String,
|
||||
key: String,
|
||||
decoded_key: Vec<u8>,
|
||||
join_handle: Option<JoinHandle<Result<(), ThreadError>>>,
|
||||
channel: Option<std::sync::mpsc::Sender<Commands>>,
|
||||
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<Commands>,
|
||||
) -> 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 {
|
||||
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![];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user