gen or load certificate

This commit is contained in:
Ferdinand Schober
2024-09-27 18:10:00 +02:00
parent 61d4a6ceac
commit 790e1b927f
6 changed files with 78 additions and 55 deletions

1
Cargo.lock generated
View File

@@ -3390,6 +3390,7 @@ dependencies = [
"log",
"p256",
"p384",
"pem",
"portable-atomic",
"rand",
"rand_core",

View File

@@ -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",

View File

@@ -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,
})
}

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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(),