mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-20 03:30:53 +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",
|
||||
"p256",
|
||||
"p384",
|
||||
"pem",
|
||||
"portable-atomic",
|
||||
"rand",
|
||||
"rand_core",
|
||||
|
||||
@@ -54,7 +54,7 @@ slab = "0.4.9"
|
||||
thiserror = "2.0.0"
|
||||
tokio-util = "0.7.11"
|
||||
local-channel = "0.1.5"
|
||||
webrtc-dtls = "0.10.0"
|
||||
webrtc-dtls = { version = "0.10.0", features = ["pem"] }
|
||||
webrtc-util = "0.9.0"
|
||||
rustls = { version = "0.23.12", default-features = false, features = [
|
||||
"std",
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::env::{self, VarError};
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::net::IpAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{collections::HashSet, io};
|
||||
use thiserror::Error;
|
||||
use toml;
|
||||
@@ -22,6 +23,8 @@ pub struct ConfigToml {
|
||||
pub port: Option<u16>,
|
||||
pub frontend: Option<Frontend>,
|
||||
pub release_bind: Option<Vec<scancode::Linux>>,
|
||||
pub pk_path: Option<PathBuf>,
|
||||
pub sk_path: Option<PathBuf>,
|
||||
pub left: Option<TomlClient>,
|
||||
pub right: Option<TomlClient>,
|
||||
pub top: Option<TomlClient>,
|
||||
@@ -40,9 +43,9 @@ pub struct TomlClient {
|
||||
}
|
||||
|
||||
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)?;
|
||||
log::info!("using config: \"{path}\"");
|
||||
log::info!("using config: \"{path:?}\"");
|
||||
Ok(toml::from_str::<_>(&config)?)
|
||||
}
|
||||
}
|
||||
@@ -81,6 +84,10 @@ struct CliArgs {
|
||||
/// emulation backend override
|
||||
#[arg(long)]
|
||||
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)]
|
||||
@@ -233,6 +240,7 @@ pub struct Config {
|
||||
pub release_bind: Vec<scancode::Linux>,
|
||||
pub test_capture: bool,
|
||||
pub test_emulation: bool,
|
||||
pub cert_path: PathBuf,
|
||||
}
|
||||
|
||||
pub struct ConfigClient {
|
||||
@@ -260,12 +268,14 @@ const DEFAULT_RELEASE_KEYS: [scancode::Linux; 4] =
|
||||
impl Config {
|
||||
pub fn new() -> Result<Self, ConfigError> {
|
||||
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)]
|
||||
let config_path = {
|
||||
let xdg_config_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))]
|
||||
@@ -275,12 +285,15 @@ impl Config {
|
||||
format!("{app_data}\\lan-mouse\\{config_file}")
|
||||
};
|
||||
|
||||
// --config <file> overrules default location
|
||||
let config_path = args.config.unwrap_or(config_path);
|
||||
let config_path = PathBuf::from(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) => {
|
||||
log::warn!("{config_path}: {e}");
|
||||
log::warn!("{config_file:?}: {e}");
|
||||
log::warn!("Continuing without config file ...");
|
||||
None
|
||||
}
|
||||
@@ -310,6 +323,11 @@ impl Config {
|
||||
.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![];
|
||||
|
||||
if let Some(config_toml) = config_toml {
|
||||
@@ -341,6 +359,7 @@ impl Config {
|
||||
release_bind,
|
||||
test_capture,
|
||||
test_emulation,
|
||||
cert_path,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::io::{self, Read};
|
||||
use std::path::PathBuf;
|
||||
use std::io::{self, BufWriter, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::{fs::File, io::BufReader};
|
||||
|
||||
use rcgen::KeyPair;
|
||||
use rustls::pki_types::CertificateDer;
|
||||
use sha2::{Digest, Sha256};
|
||||
use thiserror::Error;
|
||||
use webrtc_dtls::crypto::{Certificate, CryptoPrivateKey};
|
||||
use webrtc_dtls::crypto::Certificate;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
@@ -27,6 +25,10 @@ pub enum Error {
|
||||
Dtls(#[from] webrtc_dtls::Error),
|
||||
#[error("{0}")]
|
||||
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 {
|
||||
@@ -41,42 +43,34 @@ pub fn generate_fingerprint(cert: &[u8]) -> String {
|
||||
fingerprint
|
||||
}
|
||||
|
||||
/// 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,
|
||||
})
|
||||
pub fn certificate_fingerprint(cert: &Certificate) -> String {
|
||||
let certificate = cert.certificate.get(0).expect("certificate missing");
|
||||
generate_fingerprint(certificate)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// load certificate from file
|
||||
pub fn load_certificate(path: &Path) -> Result<Certificate, 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),
|
||||
let mut pem = String::new();
|
||||
reader.read_to_string(&mut pem)?;
|
||||
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 {
|
||||
pub(crate) async fn new(
|
||||
port: u16,
|
||||
cert: Certificate,
|
||||
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
||||
) -> Result<Self, ListenerCreationError> {
|
||||
let (listen_tx, listen_rx) = channel();
|
||||
|
||||
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(
|
||||
move |certs: &[Vec<u8>], _chains: &[CertificateDer<'static>]| {
|
||||
log::error!("verifying device fingerprint!");
|
||||
@@ -74,7 +74,7 @@ impl LanMouseListener {
|
||||
},
|
||||
));
|
||||
let cfg = Config {
|
||||
certificates: vec![certificate],
|
||||
certificates: vec![cert],
|
||||
extended_master_secret: ExtendedMasterSecretType::Require,
|
||||
client_auth: RequireAnyClientCert,
|
||||
verify_peer_certificate,
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::{
|
||||
client::ClientManager,
|
||||
config::Config,
|
||||
connect::LanMouseConnection,
|
||||
crypto,
|
||||
dns::DnsResolver,
|
||||
emulation::Emulation,
|
||||
listen::{LanMouseListener, ListenerCreationError},
|
||||
@@ -36,6 +37,8 @@ pub enum ServiceError {
|
||||
Io(#[from] io::Error),
|
||||
#[error(transparent)]
|
||||
ListenError(#[from] ListenerCreationError),
|
||||
#[error("failed to load certificate: `{0}`")]
|
||||
Certificate(#[from] crypto::Error),
|
||||
}
|
||||
|
||||
pub struct ReleaseToken;
|
||||
@@ -44,10 +47,10 @@ pub struct ReleaseToken;
|
||||
pub struct Server {
|
||||
active: Rc<Cell<Option<ClientHandle>>>,
|
||||
authorized_keys: Arc<RwLock<HashMap<String, String>>>,
|
||||
known_hosts: Rc<RefCell<HashSet<String>>>,
|
||||
_known_hosts: Rc<RefCell<HashSet<String>>>,
|
||||
pub(crate) client_manager: ClientManager,
|
||||
port: Rc<Cell<u16>>,
|
||||
public_key_fingerprint: String,
|
||||
public_key_fingerprint: Option<String>,
|
||||
notifies: Rc<Notifies>,
|
||||
pub(crate) config: Rc<Config>,
|
||||
pending_frontend_events: Rc<RefCell<VecDeque<FrontendEvent>>>,
|
||||
@@ -96,8 +99,8 @@ impl Server {
|
||||
Self {
|
||||
active: Rc::new(Cell::new(None)),
|
||||
authorized_keys: 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(),
|
||||
_known_hosts: Default::default(),
|
||||
public_key_fingerprint: None,
|
||||
config,
|
||||
client_manager,
|
||||
port,
|
||||
@@ -121,11 +124,14 @@ impl Server {
|
||||
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
|
||||
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());
|
||||
|
||||
// input capture + emulation
|
||||
@@ -264,7 +270,10 @@ impl Server {
|
||||
self.notify_frontend(FrontendEvent::CaptureStatus(self.capture_status.get()));
|
||||
self.notify_frontend(FrontendEvent::PortChanged(self.port.get(), None));
|
||||
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.authorized_keys.read().expect("lock").clone(),
|
||||
|
||||
Reference in New Issue
Block a user