mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-21 08:13:19 +03:00
gen or load certificate
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3390,6 +3390,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"p256",
|
"p256",
|
||||||
"p384",
|
"p384",
|
||||||
|
"pem",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"rand",
|
"rand",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ 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-dtls = { version = "0.10.0", features = ["pem"] }
|
||||||
webrtc-util = "0.9.0"
|
webrtc-util = "0.9.0"
|
||||||
rustls = { version = "0.23.12", default-features = false, features = [
|
rustls = { version = "0.23.12", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::env::{self, VarError};
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::{collections::HashSet, io};
|
use std::{collections::HashSet, io};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use toml;
|
use toml;
|
||||||
@@ -22,6 +23,8 @@ pub struct ConfigToml {
|
|||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub frontend: Option<Frontend>,
|
pub frontend: Option<Frontend>,
|
||||||
pub release_bind: Option<Vec<scancode::Linux>>,
|
pub release_bind: Option<Vec<scancode::Linux>>,
|
||||||
|
pub pk_path: Option<PathBuf>,
|
||||||
|
pub sk_path: Option<PathBuf>,
|
||||||
pub left: Option<TomlClient>,
|
pub left: Option<TomlClient>,
|
||||||
pub right: Option<TomlClient>,
|
pub right: Option<TomlClient>,
|
||||||
pub top: Option<TomlClient>,
|
pub top: Option<TomlClient>,
|
||||||
@@ -40,9 +43,9 @@ pub struct TomlClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigToml {
|
impl ConfigToml {
|
||||||
pub fn new(path: &str) -> Result<ConfigToml, ConfigError> {
|
pub fn new(path: &Path) -> Result<ConfigToml, ConfigError> {
|
||||||
let config = fs::read_to_string(path)?;
|
let config = fs::read_to_string(path)?;
|
||||||
log::info!("using config: \"{path}\"");
|
log::info!("using config: \"{path:?}\"");
|
||||||
Ok(toml::from_str::<_>(&config)?)
|
Ok(toml::from_str::<_>(&config)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,6 +84,10 @@ struct CliArgs {
|
|||||||
/// emulation backend override
|
/// emulation backend override
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
emulation_backend: Option<EmulationBackend>,
|
emulation_backend: Option<EmulationBackend>,
|
||||||
|
|
||||||
|
/// path to non-default certificate location
|
||||||
|
#[arg(long)]
|
||||||
|
cert_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, ValueEnum)]
|
||||||
@@ -233,6 +240,7 @@ pub struct Config {
|
|||||||
pub release_bind: Vec<scancode::Linux>,
|
pub release_bind: Vec<scancode::Linux>,
|
||||||
pub test_capture: bool,
|
pub test_capture: bool,
|
||||||
pub test_emulation: bool,
|
pub test_emulation: bool,
|
||||||
|
pub cert_path: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConfigClient {
|
pub struct ConfigClient {
|
||||||
@@ -260,12 +268,14 @@ const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
|
|||||||
impl Config {
|
impl Config {
|
||||||
pub fn new() -> Result<Self, ConfigError> {
|
pub fn new() -> Result<Self, ConfigError> {
|
||||||
let args = CliArgs::parse();
|
let args = CliArgs::parse();
|
||||||
let config_file = "config.toml";
|
const CONFIG_FILE_NAME: &str = "config.toml";
|
||||||
|
const CERT_FILE_NAME: &str = "lan-mouse.pem";
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let config_path = {
|
let config_path = {
|
||||||
let xdg_config_home =
|
let xdg_config_home =
|
||||||
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
|
env::var("XDG_CONFIG_HOME").unwrap_or(format!("{}/.config", env::var("HOME")?));
|
||||||
format!("{xdg_config_home}/lan-mouse/{config_file}")
|
format!("{xdg_config_home}/lan-mouse/")
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
@@ -275,12 +285,15 @@ impl Config {
|
|||||||
format!("{app_data}\\lan-mouse\\{config_file}")
|
format!("{app_data}\\lan-mouse\\{config_file}")
|
||||||
};
|
};
|
||||||
|
|
||||||
// --config <file> overrules default location
|
let config_path = PathBuf::from(config_path);
|
||||||
let config_path = args.config.unwrap_or(config_path);
|
let config_file = config_path.join(CONFIG_FILE_NAME);
|
||||||
|
|
||||||
let config_toml = match ConfigToml::new(config_path.as_str()) {
|
// --config <file> overrules default location
|
||||||
|
let config_file = args.config.map(PathBuf::from).unwrap_or(config_file);
|
||||||
|
|
||||||
|
let config_toml = match ConfigToml::new(&config_file) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("{config_path}: {e}");
|
log::warn!("{config_file:?}: {e}");
|
||||||
log::warn!("Continuing without config file ...");
|
log::warn!("Continuing without config file ...");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -310,6 +323,11 @@ impl Config {
|
|||||||
.emulation_backend
|
.emulation_backend
|
||||||
.or(config_toml.as_ref().and_then(|c| c.emulation_backend));
|
.or(config_toml.as_ref().and_then(|c| c.emulation_backend));
|
||||||
|
|
||||||
|
let cert_path = args
|
||||||
|
.cert_path
|
||||||
|
.or(config_toml.as_ref().and_then(|c| c.pk_path.clone()))
|
||||||
|
.unwrap_or(config_path.join(CERT_FILE_NAME));
|
||||||
|
|
||||||
let mut clients: Vec<(TomlClient, Position)> = vec![];
|
let mut clients: Vec<(TomlClient, Position)> = vec![];
|
||||||
|
|
||||||
if let Some(config_toml) = config_toml {
|
if let Some(config_toml) = config_toml {
|
||||||
@@ -341,6 +359,7 @@ impl Config {
|
|||||||
release_bind,
|
release_bind,
|
||||||
test_capture,
|
test_capture,
|
||||||
test_emulation,
|
test_emulation,
|
||||||
|
cert_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
use std::io::{self, Read};
|
use std::io::{self, BufWriter, Read, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::Path;
|
||||||
use std::{fs::File, io::BufReader};
|
use std::{fs::File, io::BufReader};
|
||||||
|
|
||||||
use rcgen::KeyPair;
|
|
||||||
use rustls::pki_types::CertificateDer;
|
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use webrtc_dtls::crypto::{Certificate, CryptoPrivateKey};
|
use webrtc_dtls::crypto::Certificate;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -27,6 +25,10 @@ pub enum Error {
|
|||||||
Dtls(#[from] webrtc_dtls::Error),
|
Dtls(#[from] webrtc_dtls::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Other(String),
|
Other(String),
|
||||||
|
#[error("a file containing the public key is missing")]
|
||||||
|
PkMissing,
|
||||||
|
#[error("a file containing the private key is missing")]
|
||||||
|
SkMissing,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_fingerprint(cert: &[u8]) -> String {
|
pub fn generate_fingerprint(cert: &[u8]) -> String {
|
||||||
@@ -41,42 +43,34 @@ pub fn generate_fingerprint(cert: &[u8]) -> String {
|
|||||||
fingerprint
|
fingerprint
|
||||||
}
|
}
|
||||||
|
|
||||||
/// load_key_and_certificate reads certificates or key from file
|
pub fn certificate_fingerprint(cert: &Certificate) -> String {
|
||||||
pub fn load_key_and_certificate(
|
let certificate = cert.certificate.get(0).expect("certificate missing");
|
||||||
key_path: PathBuf,
|
generate_fingerprint(certificate)
|
||||||
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
|
/// load certificate from file
|
||||||
pub fn load_key(path: PathBuf) -> Result<CryptoPrivateKey, Error> {
|
pub fn load_certificate(path: &Path) -> Result<Certificate, 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 f = File::open(path)?;
|
||||||
|
|
||||||
let mut reader = BufReader::new(f);
|
let mut reader = BufReader::new(f);
|
||||||
match rustls_pemfile::certs(&mut reader).collect::<Result<Vec<_>, _>>() {
|
let mut pem = String::new();
|
||||||
Ok(certs) => Ok(certs.into_iter().map(CertificateDer::from).collect()),
|
reader.read_to_string(&mut pem)?;
|
||||||
Err(_) => Err(Error::ErrNoCertificateFound),
|
Ok(Certificate::from_pem(pem.as_str())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_or_generate_key_and_cert(path: &Path) -> Result<Certificate, Error> {
|
||||||
|
if path.exists() && path.is_file() {
|
||||||
|
return Ok(load_certificate(path)?);
|
||||||
|
} else {
|
||||||
|
return Ok(generate_key_and_cert(path)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_key_and_cert(path: &Path) -> Result<Certificate, Error> {
|
||||||
|
let cert = Certificate::generate_self_signed(["ignored".to_owned()])?;
|
||||||
|
let serialized = cert.serialize_pem();
|
||||||
|
let f = File::create(path)?;
|
||||||
|
let mut writer = BufWriter::new(f);
|
||||||
|
writer.write(serialized.as_bytes())?;
|
||||||
|
Ok(cert)
|
||||||
|
}
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ type VerifyPeerCertificateFn = Arc<
|
|||||||
impl LanMouseListener {
|
impl LanMouseListener {
|
||||||
pub(crate) async fn new(
|
pub(crate) async fn new(
|
||||||
port: u16,
|
port: u16,
|
||||||
|
cert: Certificate,
|
||||||
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
||||||
) -> Result<Self, ListenerCreationError> {
|
) -> Result<Self, ListenerCreationError> {
|
||||||
let (listen_tx, listen_rx) = channel();
|
let (listen_tx, listen_rx) = channel();
|
||||||
|
|
||||||
let listen_addr = SocketAddr::new("0.0.0.0".parse().expect("invalid ip"), port);
|
let listen_addr = SocketAddr::new("0.0.0.0".parse().expect("invalid ip"), port);
|
||||||
let certificate = Certificate::generate_self_signed(["localhost".to_owned()])?;
|
|
||||||
let verify_peer_certificate: Option<VerifyPeerCertificateFn> = Some(Arc::new(
|
let verify_peer_certificate: Option<VerifyPeerCertificateFn> = Some(Arc::new(
|
||||||
move |certs: &[Vec<u8>], _chains: &[CertificateDer<'static>]| {
|
move |certs: &[Vec<u8>], _chains: &[CertificateDer<'static>]| {
|
||||||
log::error!("verifying device fingerprint!");
|
log::error!("verifying device fingerprint!");
|
||||||
@@ -74,7 +74,7 @@ impl LanMouseListener {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
let cfg = Config {
|
let cfg = Config {
|
||||||
certificates: vec![certificate],
|
certificates: vec![cert],
|
||||||
extended_master_secret: ExtendedMasterSecretType::Require,
|
extended_master_secret: ExtendedMasterSecretType::Require,
|
||||||
client_auth: RequireAnyClientCert,
|
client_auth: RequireAnyClientCert,
|
||||||
verify_peer_certificate,
|
verify_peer_certificate,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use crate::{
|
|||||||
client::ClientManager,
|
client::ClientManager,
|
||||||
config::Config,
|
config::Config,
|
||||||
connect::LanMouseConnection,
|
connect::LanMouseConnection,
|
||||||
|
crypto,
|
||||||
dns::DnsResolver,
|
dns::DnsResolver,
|
||||||
emulation::Emulation,
|
emulation::Emulation,
|
||||||
listen::{LanMouseListener, ListenerCreationError},
|
listen::{LanMouseListener, ListenerCreationError},
|
||||||
@@ -36,6 +37,8 @@ pub enum ServiceError {
|
|||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ListenError(#[from] ListenerCreationError),
|
ListenError(#[from] ListenerCreationError),
|
||||||
|
#[error("failed to load certificate: `{0}`")]
|
||||||
|
Certificate(#[from] crypto::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReleaseToken;
|
pub struct ReleaseToken;
|
||||||
@@ -44,10 +47,10 @@ pub struct ReleaseToken;
|
|||||||
pub struct Server {
|
pub struct Server {
|
||||||
active: Rc<Cell<Option<ClientHandle>>>,
|
active: Rc<Cell<Option<ClientHandle>>>,
|
||||||
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
||||||
known_hosts: Rc<RefCell<HashSet<String>>>,
|
_known_hosts: Rc<RefCell<HashSet<String>>>,
|
||||||
pub(crate) client_manager: ClientManager,
|
pub(crate) client_manager: ClientManager,
|
||||||
port: Rc<Cell<u16>>,
|
port: Rc<Cell<u16>>,
|
||||||
public_key_fingerprint: String,
|
public_key_fingerprint: Option<String>,
|
||||||
notifies: Rc<Notifies>,
|
notifies: Rc<Notifies>,
|
||||||
pub(crate) config: Rc<Config>,
|
pub(crate) config: Rc<Config>,
|
||||||
pending_frontend_events: Rc<RefCell<VecDeque<FrontendEvent>>>,
|
pending_frontend_events: Rc<RefCell<VecDeque<FrontendEvent>>>,
|
||||||
@@ -96,8 +99,8 @@ impl Server {
|
|||||||
Self {
|
Self {
|
||||||
active: Rc::new(Cell::new(None)),
|
active: Rc::new(Cell::new(None)),
|
||||||
authorized_keys: Default::default(),
|
authorized_keys: Default::default(),
|
||||||
known_hosts: Default::default(),
|
_known_hosts: Default::default(),
|
||||||
public_key_fingerprint: "87:f9:d9:a6:c4:a1:14:d2:c8:25:4f:72:b7:01:86:65:73:cc:bc:a1:37:cc:96:69:f8:f4:72:8a:60:9a:3b:4d".to_owned(),
|
public_key_fingerprint: None,
|
||||||
config,
|
config,
|
||||||
client_manager,
|
client_manager,
|
||||||
port,
|
port,
|
||||||
@@ -121,11 +124,14 @@ impl Server {
|
|||||||
e => e?,
|
e => e?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// let cert = crypto::load_key_and_certificate(config.sk_path, config.pk_path)?;
|
// load certificate
|
||||||
|
let cert = crypto::load_or_generate_key_and_cert(&self.config.cert_path)?;
|
||||||
|
let public_key_fingerprint = crypto::certificate_fingerprint(&cert);
|
||||||
|
self.public_key_fingerprint.replace(public_key_fingerprint);
|
||||||
|
|
||||||
// listener + connection
|
// listener + connection
|
||||||
let listener =
|
let listener =
|
||||||
LanMouseListener::new(self.config.port, self.authorized_keys.clone()).await?;
|
LanMouseListener::new(self.config.port, cert, self.authorized_keys.clone()).await?;
|
||||||
let conn = LanMouseConnection::new(self.clone());
|
let conn = LanMouseConnection::new(self.clone());
|
||||||
|
|
||||||
// input capture + emulation
|
// input capture + emulation
|
||||||
@@ -264,7 +270,10 @@ impl Server {
|
|||||||
self.notify_frontend(FrontendEvent::CaptureStatus(self.capture_status.get()));
|
self.notify_frontend(FrontendEvent::CaptureStatus(self.capture_status.get()));
|
||||||
self.notify_frontend(FrontendEvent::PortChanged(self.port.get(), None));
|
self.notify_frontend(FrontendEvent::PortChanged(self.port.get(), None));
|
||||||
self.notify_frontend(FrontendEvent::PublicKeyFingerprint(
|
self.notify_frontend(FrontendEvent::PublicKeyFingerprint(
|
||||||
self.public_key_fingerprint.clone(),
|
self.public_key_fingerprint
|
||||||
|
.as_ref()
|
||||||
|
.expect("fingerprint")
|
||||||
|
.clone(),
|
||||||
));
|
));
|
||||||
self.notify_frontend(FrontendEvent::AuthorizedUpdated(
|
self.notify_frontend(FrontendEvent::AuthorizedUpdated(
|
||||||
self.authorized_keys.read().expect("lock").clone(),
|
self.authorized_keys.read().expect("lock").clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user