mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-14 20:41:27 +03:00
start working on encryption
This commit is contained in:
committed by
Ferdinand Schober
parent
0d074e19f1
commit
79bc64e56e
5
.gitignore
vendored
5
.gitignore
vendored
@@ -4,4 +4,7 @@
|
|||||||
.vs/
|
.vs/
|
||||||
.vscode/
|
.vscode/
|
||||||
.direnv/
|
.direnv/
|
||||||
result
|
result
|
||||||
|
*.pem
|
||||||
|
*.csr
|
||||||
|
extfile.conf
|
||||||
|
|||||||
1043
Cargo.lock
generated
1043
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,14 @@ slab = "0.4.9"
|
|||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
tokio-util = "0.7.11"
|
tokio-util = "0.7.11"
|
||||||
local-channel = "0.1.5"
|
local-channel = "0.1.5"
|
||||||
|
webrtc-dtls = "0.10.0"
|
||||||
|
webrtc-util = "0.9.0"
|
||||||
|
rustls = { version = "0.23.12", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"ring",
|
||||||
|
] }
|
||||||
|
rcgen = "0.13.1"
|
||||||
|
rustls-pemfile = "2.1.3"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2.148"
|
libc = "0.2.148"
|
||||||
|
|||||||
74
gen-key.sh
Executable file
74
gen-key.sh
Executable file
@@ -0,0 +1,74 @@
|
|||||||
|
export SERVER_NAME="$1"
|
||||||
|
export CLIENT_NAME="$2"
|
||||||
|
export EXTFILE='extfile.conf'
|
||||||
|
|
||||||
|
if [ -z "${CLIENT_NAME}" ]; then
|
||||||
|
echo "usage: $0 <client-name> <server-name>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${SERVER_NAME}" ]; then
|
||||||
|
echo "usage: $0 <client-name> <server-name>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo 'subjectAltName = DNS:iridium' > "${EXTFILE}"
|
||||||
|
|
||||||
|
# server
|
||||||
|
|
||||||
|
## generate EC private key
|
||||||
|
openssl ecparam \
|
||||||
|
-name prime256v1 \
|
||||||
|
-genkey \
|
||||||
|
-noout \
|
||||||
|
-out "${SERVER_NAME}.pem"
|
||||||
|
|
||||||
|
## generate certificate signing request
|
||||||
|
openssl req \
|
||||||
|
-new \
|
||||||
|
-key "${SERVER_NAME}.pem" \
|
||||||
|
-sha256 \
|
||||||
|
-subj '/C=NL' \
|
||||||
|
-out "${SERVER_NAME}.csr"
|
||||||
|
|
||||||
|
## generate CA certificate (server public key)
|
||||||
|
openssl x509 \
|
||||||
|
-req \
|
||||||
|
-in "${SERVER_NAME}.csr" \
|
||||||
|
-extfile "${EXTFILE}" \
|
||||||
|
-days 365 \
|
||||||
|
-signkey "${SERVER_NAME}.pem" \
|
||||||
|
-sha256 \
|
||||||
|
-out "${SERVER_NAME}.pub.pem"
|
||||||
|
|
||||||
|
|
||||||
|
# client
|
||||||
|
## generate client private key
|
||||||
|
openssl ecparam \
|
||||||
|
-name prime256v1 \
|
||||||
|
-genkey \
|
||||||
|
-noout \
|
||||||
|
-out "${CLIENT_NAME}.pem"
|
||||||
|
|
||||||
|
## generate client csr (= public key)
|
||||||
|
openssl req \
|
||||||
|
-key "${CLIENT_NAME}.pem" \
|
||||||
|
-new -sha256 \
|
||||||
|
-subj '/C=NL' \
|
||||||
|
-out "${CLIENT_NAME}.csr"
|
||||||
|
|
||||||
|
## generate client certificate (=public key signed by CA)
|
||||||
|
openssl x509 \
|
||||||
|
-req \
|
||||||
|
-in "${CLIENT_NAME}.csr" \
|
||||||
|
-extfile "${EXTFILE}" \
|
||||||
|
-days 365 \
|
||||||
|
-CA "${SERVER_NAME}.pub.pem" \
|
||||||
|
-CAkey "${SERVER_NAME}.pem" \
|
||||||
|
-set_serial '0xabcd' \
|
||||||
|
-sha256 -out "${CLIENT_NAME}.pub.pem"
|
||||||
|
|
||||||
|
# cleanup
|
||||||
|
rm "${EXTFILE}" \
|
||||||
|
"${SERVER_NAME}.csr" \
|
||||||
|
"${CLIENT_NAME}.csr"
|
||||||
69
src/crypto.rs
Normal file
69
src/crypto.rs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
use std::io::{self, Read};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs::File, io::BufReader};
|
||||||
|
|
||||||
|
use rcgen::KeyPair;
|
||||||
|
use rustls::pki_types::CertificateDer;
|
||||||
|
use thiserror::Error;
|
||||||
|
use webrtc_dtls::crypto::{Certificate, CryptoPrivateKey};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("block is not a private key, unable to load key")]
|
||||||
|
ErrBlockIsNotPrivateKey,
|
||||||
|
#[error("unknown key time in PKCS#8 wrapping, unable to load key")]
|
||||||
|
ErrUnknownKeyTime,
|
||||||
|
#[error("no private key found, unable to load key")]
|
||||||
|
ErrNoPrivateKeyFound,
|
||||||
|
#[error("block is not a certificate, unable to load certificates")]
|
||||||
|
ErrBlockIsNotCertificate,
|
||||||
|
#[error("no certificate found, unable to load certificates")]
|
||||||
|
ErrNoCertificateFound,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
Dtls(#[from] webrtc_dtls::Error),
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// load_key_and_certificate reads certificates or key from file
|
||||||
|
pub fn load_key_and_certificate(
|
||||||
|
key_path: PathBuf,
|
||||||
|
certificate_path: PathBuf,
|
||||||
|
) -> Result<Certificate, Error> {
|
||||||
|
let private_key = load_key(key_path)?;
|
||||||
|
|
||||||
|
let certificate = load_certificate(certificate_path)?;
|
||||||
|
|
||||||
|
Ok(Certificate {
|
||||||
|
certificate,
|
||||||
|
private_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// load_key Load/read key from file
|
||||||
|
pub fn load_key(path: PathBuf) -> Result<CryptoPrivateKey, Error> {
|
||||||
|
let f = File::open(path)?;
|
||||||
|
let mut reader = BufReader::new(f);
|
||||||
|
let mut buf = vec![];
|
||||||
|
reader.read_to_end(&mut buf)?;
|
||||||
|
|
||||||
|
let s = String::from_utf8(buf).expect("utf8 of file");
|
||||||
|
|
||||||
|
let key_pair = KeyPair::from_pem(s.as_str()).expect("key pair in file");
|
||||||
|
|
||||||
|
Ok(CryptoPrivateKey::from_key_pair(&key_pair).expect("crypto key pair"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// load_certificate Load/read certificate(s) from file
|
||||||
|
pub fn load_certificate(path: PathBuf) -> Result<Vec<CertificateDer<'static>>, Error> {
|
||||||
|
let f = File::open(path)?;
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(f);
|
||||||
|
match rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>() {
|
||||||
|
Ok(certs) => Ok(certs.into_iter().map(CertificateDer::from).collect()),
|
||||||
|
Err(_) => Err(Error::ErrNoCertificateFound),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,4 +4,5 @@ pub mod dns;
|
|||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
pub mod capture_test;
|
pub mod capture_test;
|
||||||
|
pub mod crypto;
|
||||||
pub mod emulation_test;
|
pub mod emulation_test;
|
||||||
|
|||||||
6
src/plugin.rs
Normal file
6
src/plugin.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use input_capture::CaptureEvent;
|
||||||
|
|
||||||
|
struct PluginManager {
|
||||||
|
capture_hook: Vec<Box<dyn Fn(CaptureEvent)>>,
|
||||||
|
capture_transform: Vec<Box<dyn Fn(CaptureEvent) -> CaptureEvent>>,
|
||||||
|
}
|
||||||
@@ -159,6 +159,7 @@ async fn handle_capture_event(
|
|||||||
/* released capture */
|
/* released capture */
|
||||||
State::Receiving => ProtoEvent::Leave(0),
|
State::Receiving => ProtoEvent::Leave(0),
|
||||||
};
|
};
|
||||||
|
log::error!("SENDING: {event:?} -> {addr:?}");
|
||||||
sender_tx.send((event, addr)).expect("sender closed");
|
sender_tx.send((event, addr)).expect("sender closed");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
use local_channel::mpsc::{Receiver, Sender};
|
use local_channel::mpsc::{Receiver, Sender};
|
||||||
use std::{io, net::SocketAddr};
|
use std::{cell::RefCell, collections::HashMap, io, net::SocketAddr, rc::Rc, sync::Arc};
|
||||||
|
use webrtc_dtls::{
|
||||||
|
config::{ClientAuthType, Config, ExtendedMasterSecretType},
|
||||||
|
conn::DTLSConn,
|
||||||
|
listener::listen,
|
||||||
|
};
|
||||||
|
use webrtc_util::{conn::Listener, Conn};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::{net::UdpSocket, task::JoinHandle};
|
use tokio::{
|
||||||
|
net::UdpSocket,
|
||||||
|
task::{spawn_local, JoinHandle},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::crypto;
|
||||||
|
|
||||||
use super::Server;
|
use super::Server;
|
||||||
use lan_mouse_proto::{ProtoEvent, ProtocolError};
|
use lan_mouse_proto::{ProtoEvent, ProtocolError};
|
||||||
@@ -14,64 +25,94 @@ pub(crate) async fn new(
|
|||||||
) -> io::Result<JoinHandle<()>> {
|
) -> io::Result<JoinHandle<()>> {
|
||||||
// bind the udp socket
|
// bind the udp socket
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), server.port.get());
|
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), server.port.get());
|
||||||
let mut socket = UdpSocket::bind(listen_addr).await?;
|
|
||||||
|
|
||||||
Ok(tokio::task::spawn_local(async move {
|
Ok(spawn_local(async move {
|
||||||
let mut sender_rx = udp_send_rx;
|
let sender_rx = Rc::new(RefCell::new(udp_send_rx));
|
||||||
loop {
|
loop {
|
||||||
let udp_receiver = udp_receiver(&socket, &udp_recv_tx);
|
let udp_receiver = spawn_local(listen_dtls(listen_addr, udp_recv_tx.clone()));
|
||||||
let udp_sender = udp_sender(&socket, &mut sender_rx);
|
let udp_sender = spawn_local(udp_sender(sender_rx.clone()));
|
||||||
|
log::info!("starting sender + receiver");
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = udp_receiver => break, /* channel closed */
|
e = udp_receiver => panic!("{e:?}"), /* channel closed */
|
||||||
_ = udp_sender => break, /* channel closed */
|
_ = udp_sender => break, /* channel closed */
|
||||||
_ = server.notifies.port_changed.notified() => update_port(&server, &mut socket).await,
|
|
||||||
_ = server.cancelled() => break, /* cancellation requested */
|
_ = server.cancelled() => break, /* cancellation requested */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_port(server: &Server, socket: &mut UdpSocket) {
|
async fn listen_dtls(
|
||||||
let new_port = server.port.get();
|
listen_addr: SocketAddr,
|
||||||
let current_port = socket.local_addr().expect("socket not bound").port();
|
udp_recv_tx: Sender<Result<(ProtoEvent, SocketAddr), NetworkError>>,
|
||||||
|
) -> Result<(), NetworkError> {
|
||||||
// if port is the same, we dont need to change it
|
let server_cert = crypto::load_key_and_certificate("alice.pem".into(), "alice.pub.pem".into())?;
|
||||||
if current_port == new_port {
|
let mut cert_pool = rustls::RootCertStore::empty();
|
||||||
return;
|
let certs = crypto::load_certificate("alice.pub.pem".into())?;
|
||||||
|
for cert in certs.into_iter() {
|
||||||
|
cert_pool.add(cert)?;
|
||||||
}
|
}
|
||||||
|
let cfg = Config {
|
||||||
// bind new socket
|
certificates: vec![server_cert],
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().unwrap(), new_port);
|
extended_master_secret: ExtendedMasterSecretType::Require,
|
||||||
let new_socket = UdpSocket::bind(listen_addr).await;
|
client_auth: ClientAuthType::RequireAndVerifyClientCert,
|
||||||
let err = match new_socket {
|
client_cas: cert_pool,
|
||||||
Ok(new_socket) => {
|
..Default::default()
|
||||||
*socket = new_socket;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Err(e) => Some(e.to_string()),
|
|
||||||
};
|
};
|
||||||
|
let listener = Arc::new(listen(listen_addr, cfg).await?);
|
||||||
// notify frontend of the actual port
|
|
||||||
let port = socket.local_addr().expect("socket not bound").port();
|
|
||||||
server.notify_port_changed(port, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn udp_receiver(
|
|
||||||
socket: &UdpSocket,
|
|
||||||
receiver_tx: &Sender<Result<(ProtoEvent, SocketAddr), NetworkError>>,
|
|
||||||
) {
|
|
||||||
loop {
|
loop {
|
||||||
let event = receive_event(socket).await;
|
while let Ok((conn, addr)) = listener.accept().await {
|
||||||
receiver_tx.send(event).expect("channel closed");
|
let udp_recv_tx = udp_recv_tx.clone();
|
||||||
|
spawn_local(async move {
|
||||||
|
loop {
|
||||||
|
let mut buf = [0u8; lan_mouse_proto::MAX_EVENT_SIZE];
|
||||||
|
let event: Result<_, NetworkError> = match conn.recv(&mut buf).await {
|
||||||
|
Ok(_len) => match ProtoEvent::try_from(buf) {
|
||||||
|
Ok(e) => Ok((e, addr)),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
},
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
};
|
||||||
|
udp_recv_tx.send(event).expect("channel closed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn udp_sender(socket: &UdpSocket, rx: &mut Receiver<(ProtoEvent, SocketAddr)>) {
|
async fn udp_sender(rx: Rc<RefCell<Receiver<(ProtoEvent, SocketAddr)>>>) {
|
||||||
|
let mut connection_pool: HashMap<SocketAddr, DTLSConn> = HashMap::new();
|
||||||
loop {
|
loop {
|
||||||
let (event, addr) = rx.recv().await.expect("channel closed");
|
log::error!("waiting for event to send ...");
|
||||||
if let Err(e) = send_event(socket, event, addr) {
|
let (event, addr) = rx.borrow_mut().recv().await.expect("channel closed");
|
||||||
log::warn!("udp send failed: {e}");
|
log::error!("{:20} ------>->->-> {addr}", event.to_string());
|
||||||
|
if !connection_pool.contains_key(&addr) {
|
||||||
|
let socket = Arc::new(UdpSocket::bind("0.0.0.0:0").await.unwrap());
|
||||||
|
socket.connect(addr).await.unwrap();
|
||||||
|
let certificate =
|
||||||
|
crypto::load_key_and_certificate("bob.pem".into(), "bob.pub.pem".into()).unwrap();
|
||||||
|
let mut cert_pool = rustls::RootCertStore::empty();
|
||||||
|
let certs = crypto::load_certificate("alice.pub.pem".into()).unwrap();
|
||||||
|
for cert in certs.into_iter() {
|
||||||
|
cert_pool.add(cert.to_owned()).unwrap();
|
||||||
|
}
|
||||||
|
let config = Config {
|
||||||
|
certificates: vec![certificate],
|
||||||
|
extended_master_secret: ExtendedMasterSecretType::Require,
|
||||||
|
roots_cas: cert_pool,
|
||||||
|
server_name: "webrtc.rs".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
log::error!("connecting to {addr}");
|
||||||
|
let conn = DTLSConn::new(socket, config, true, None).await.unwrap();
|
||||||
|
log::error!("connected {addr}!");
|
||||||
|
connection_pool.insert(addr, conn);
|
||||||
};
|
};
|
||||||
|
let conn = connection_pool.get(&addr).unwrap();
|
||||||
|
log::error!("{:20} ------>->->-> {addr}", event.to_string());
|
||||||
|
let (data, len): ([u8; lan_mouse_proto::MAX_EVENT_SIZE], usize) = event.into();
|
||||||
|
// When udp blocks, we dont want to block the event loop.
|
||||||
|
// Dropping events is better than potentially crashing the input capture.
|
||||||
|
conn.send_to(&data[..len], addr).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,19 +122,12 @@ pub(crate) enum NetworkError {
|
|||||||
Protocol(#[from] ProtocolError),
|
Protocol(#[from] ProtocolError),
|
||||||
#[error("network error: `{0}`")]
|
#[error("network error: `{0}`")]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
}
|
#[error(transparent)]
|
||||||
|
Crypt(#[from] crypto::Error),
|
||||||
async fn receive_event(socket: &UdpSocket) -> Result<(ProtoEvent, SocketAddr), NetworkError> {
|
#[error(transparent)]
|
||||||
let mut buf = [0u8; lan_mouse_proto::MAX_EVENT_SIZE];
|
Rustls(#[from] rustls::Error),
|
||||||
let (_len, src) = socket.recv_from(&mut buf).await?;
|
#[error(transparent)]
|
||||||
let event = ProtoEvent::try_from(buf)?;
|
WebrtcDtls(#[from] webrtc_dtls::Error),
|
||||||
Ok((event, src))
|
#[error(transparent)]
|
||||||
}
|
WebrtcUtil(#[from] webrtc_util::Error),
|
||||||
|
|
||||||
fn send_event(sock: &UdpSocket, e: ProtoEvent, addr: SocketAddr) -> Result<usize, NetworkError> {
|
|
||||||
log::trace!("{:20} ------>->->-> {addr}", e.to_string());
|
|
||||||
let (data, len): ([u8; lan_mouse_proto::MAX_EVENT_SIZE], usize) = e.into();
|
|
||||||
// When udp blocks, we dont want to block the event loop.
|
|
||||||
// Dropping events is better than potentially crashing the input capture.
|
|
||||||
Ok(sock.try_send_to(&data[..len], addr)?)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user