add support for session encryption
This commit is contained in:
8 files changed
+140
-12
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
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]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -988,6 +1012,12 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dunce"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.36.2"
|
version = "0.36.2"
|
||||||
@@ -1374,6 +1404,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fs_extra"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.34"
|
version = "0.3.34"
|
||||||
@@ -3527,6 +3563,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"cpal",
|
"cpal",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"hex",
|
||||||
"log",
|
"log",
|
||||||
"tap_core",
|
"tap_core",
|
||||||
]
|
]
|
||||||
@@ -3546,9 +3583,11 @@ dependencies = [
|
|||||||
name = "tap_core"
|
name = "tap_core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aws-lc-rs",
|
||||||
"chrono",
|
"chrono",
|
||||||
"circular-buffer",
|
"circular-buffer",
|
||||||
"cpal",
|
"cpal",
|
||||||
|
"hex",
|
||||||
"log",
|
"log",
|
||||||
"opus",
|
"opus",
|
||||||
"opusic-sys",
|
"opusic-sys",
|
||||||
@@ -3565,6 +3604,7 @@ dependencies = [
|
|||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"hex",
|
||||||
"log",
|
"log",
|
||||||
"tap_core",
|
"tap_core",
|
||||||
"thiserror 2.0.21",
|
"thiserror 2.0.21",
|
||||||
@@ -3802,6 +3842,12 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.8"
|
version = "2.5.8"
|
||||||
@@ -4947,6 +4993,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ log = "0.4.27"
|
|||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
tap_core = {path = "tap_core"}
|
tap_core = {path = "tap_core"}
|
||||||
cpal = "0.18.2"
|
cpal = "0.18.2"
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["tap_core", "tap_gui"]
|
members = ["tap_core", "tap_gui"]
|
||||||
|
|||||||
+22
-2
@@ -23,6 +23,8 @@ enum Tasks {
|
|||||||
src: SocketAddr,
|
src: SocketAddr,
|
||||||
/// UDP socket addr to connect to
|
/// UDP socket addr to connect to
|
||||||
dest: SocketAddr,
|
dest: SocketAddr,
|
||||||
|
/// Session key to use
|
||||||
|
session_key: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +38,12 @@ fn main() {
|
|||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
|
|
||||||
match opt.command {
|
match opt.command {
|
||||||
Tasks::Tap { device, src, dest } => {
|
Tasks::Tap {
|
||||||
|
device,
|
||||||
|
src,
|
||||||
|
dest,
|
||||||
|
session_key,
|
||||||
|
} => {
|
||||||
let udp_connection = match OpusUdpConnection::new(src, dest) {
|
let udp_connection = match OpusUdpConnection::new(src, dest) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -49,7 +56,20 @@ fn main() {
|
|||||||
|
|
||||||
let audio_source_thread = AudioSourceThread::new(device, host, receiver);
|
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}");
|
error!("Error running tap: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,3 +13,5 @@ tap-interface = {version = "0.1.0", registry = "ahines"}
|
|||||||
thiserror = "2.0.21"
|
thiserror = "2.0.21"
|
||||||
opus= { version="0.4.0" }
|
opus= { version="0.4.0" }
|
||||||
opusic-sys = { version = "0.7.5", features = ["bundled", "no-stack-protector", "no-hardening", "no-fortify-source"] }
|
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)
|
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 encoder = Self::setup_encoder()?;
|
||||||
let (sender, receiver) = channel();
|
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 thread = thread::spawn(move || tx_thread.thread());
|
||||||
|
|
||||||
let mut stream = self.setup_stream(&sender)?;
|
let mut stream = self.setup_stream(&sender)?;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::util::STEREO_FRAME_SIZE;
|
use crate::util::STEREO_FRAME_SIZE;
|
||||||
use crate::util::{CIRC_BUFFER_SIZE, TAP_PACKET_MAX_DATA_LEN, ThreadMessage};
|
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 chrono::Utc;
|
||||||
use circular_buffer::FixedCircularBuffer;
|
use circular_buffer::FixedCircularBuffer;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
@@ -18,6 +20,8 @@ pub enum Error {
|
|||||||
IoFailure(#[from] std::io::Error),
|
IoFailure(#[from] std::io::Error),
|
||||||
#[error("Encode error: {0}")]
|
#[error("Encode error: {0}")]
|
||||||
EncodeFailure(#[from] opus::Error),
|
EncodeFailure(#[from] opus::Error),
|
||||||
|
#[error("Failed to encrypt data")]
|
||||||
|
EncryptionFailed(#[from] Unspecified),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TxThread<T: Read + Write> {
|
pub struct TxThread<T: Read + Write> {
|
||||||
@@ -27,10 +31,16 @@ pub struct TxThread<T: Read + Write> {
|
|||||||
circular_buffer: FixedCircularBuffer<f32, CIRC_BUFFER_SIZE>,
|
circular_buffer: FixedCircularBuffer<f32, CIRC_BUFFER_SIZE>,
|
||||||
seq_count: u32,
|
seq_count: u32,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
|
key: RandomizedNonceKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read + Write> TxThread<T> {
|
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 {
|
Self {
|
||||||
receiver,
|
receiver,
|
||||||
encoder,
|
encoder,
|
||||||
@@ -38,6 +48,7 @@ impl<T: Read + Write> TxThread<T> {
|
|||||||
circular_buffer: Default::default(),
|
circular_buffer: Default::default(),
|
||||||
seq_count: 0,
|
seq_count: 0,
|
||||||
exit: false,
|
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)?)
|
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> {
|
fn send_opus_frame(&mut self) -> Result<(), Error> {
|
||||||
let mut output_buffer = vec![0; TAP_PACKET_MAX_DATA_LEN];
|
let mut output_buffer = vec![0; TAP_PACKET_MAX_DATA_LEN];
|
||||||
let size = self.encode(&mut output_buffer)?;
|
let size = self.encode(&mut output_buffer)?;
|
||||||
@@ -58,8 +81,11 @@ impl<T: Read + Write> TxThread<T> {
|
|||||||
audio_data: output_buffer,
|
audio_data: output_buffer,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.opus_connection
|
let tap_message = tap_message.encode_to_vec();
|
||||||
.write_all(tap_message.encode_to_vec().as_slice())?;
|
|
||||||
|
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);
|
(self.seq_count, _) = self.seq_count.overflowing_add(1);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ egui = "0.36.2"
|
|||||||
eframe = "0.36.2"
|
eframe = "0.36.2"
|
||||||
tokio = { version = "1.53.2", features = ["rt", "rt-multi-thread", "macros"] }
|
tokio = { version = "1.53.2", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
thiserror = "2.0.21"
|
thiserror = "2.0.21"
|
||||||
|
hex = "0.4.3"
|
||||||
+27
-5
@@ -31,6 +31,8 @@ pub struct Tap {
|
|||||||
device: String,
|
device: String,
|
||||||
denny_jack_host: String,
|
denny_jack_host: String,
|
||||||
warning: String,
|
warning: String,
|
||||||
|
key: String,
|
||||||
|
decoded_key: Vec<u8>,
|
||||||
join_handle: Option<JoinHandle<Result<(), ThreadError>>>,
|
join_handle: Option<JoinHandle<Result<(), ThreadError>>>,
|
||||||
channel: Option<std::sync::mpsc::Sender<Commands>>,
|
channel: Option<std::sync::mpsc::Sender<Commands>>,
|
||||||
host: cpal::Host,
|
host: cpal::Host,
|
||||||
@@ -49,6 +51,8 @@ impl Tap {
|
|||||||
device: "".to_string(),
|
device: "".to_string(),
|
||||||
denny_jack_host: "158.69.121.46:9999".to_string(),
|
denny_jack_host: "158.69.121.46:9999".to_string(),
|
||||||
warning: "".to_string(),
|
warning: "".to_string(),
|
||||||
|
key: "".to_string(),
|
||||||
|
decoded_key: vec![],
|
||||||
join_handle: None,
|
join_handle: None,
|
||||||
channel: None,
|
channel: None,
|
||||||
host: cpal::default_host(),
|
host: cpal::default_host(),
|
||||||
@@ -58,6 +62,7 @@ impl Tap {
|
|||||||
pub fn bg_threads(
|
pub fn bg_threads(
|
||||||
device: &str,
|
device: &str,
|
||||||
host: &str,
|
host: &str,
|
||||||
|
key: &[u8],
|
||||||
channel: Receiver<Commands>,
|
channel: Receiver<Commands>,
|
||||||
) -> Result<(), ThreadError> {
|
) -> Result<(), ThreadError> {
|
||||||
let udp_connection =
|
let udp_connection =
|
||||||
@@ -72,7 +77,7 @@ impl Tap {
|
|||||||
let audio_source_thread = AudioSourceThread::new(device.to_string(), host, channel);
|
let audio_source_thread = AudioSourceThread::new(device.to_string(), host, channel);
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
audio_source_thread.worker(udp_connection)?;
|
audio_source_thread.worker(udp_connection, key)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -89,6 +94,9 @@ impl App for Tap {
|
|||||||
State::Init => {
|
State::Init => {
|
||||||
ui.label("Denny Jack Host");
|
ui.label("Denny Jack Host");
|
||||||
ui.text_edit_singleline(&mut self.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")
|
egui::containers::ComboBox::from_label("Device")
|
||||||
.selected_text(&self.device)
|
.selected_text(&self.device)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
@@ -102,11 +110,21 @@ impl App for Tap {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if ui.button("Connect").clicked() {
|
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();
|
self.warning = "Enter connection details and try again".to_string();
|
||||||
} else {
|
} else {
|
||||||
self.warning = "".to_string();
|
if let Ok(key) = hex::decode(&self.key)
|
||||||
self.mode = State::Connecting;
|
&& 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 (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.join_handle = Some(join);
|
||||||
self.channel = Some(sender);
|
self.channel = Some(sender);
|
||||||
@@ -148,6 +169,7 @@ impl App for Tap {
|
|||||||
let _ = handle.join();
|
let _ = handle.join();
|
||||||
self.join_handle = None;
|
self.join_handle = None;
|
||||||
self.mode = State::Init;
|
self.mode = State::Init;
|
||||||
|
self.decoded_key = vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user