use clap::Parser;
use qssh::server::{QsshServer, QsshServerConfig};
use std::process;
use std::path::PathBuf;
use tokio::fs;
use base64::Engine;
#[derive(Parser, Debug)]
#[clap(name = "qsshd")]
#[clap(about = "Quantum-Secure Shell Daemon - Accept quantum-secure remote connections")]
#[clap(version = "0.1.0")]
struct Args {
#[clap(short, long, default_value = "0.0.0.0:4242")]
listen: String,
#[clap(long)]
generate_keys: bool,
#[clap(long, default_value = "/etc/qssh/host_key")]
host_key: PathBuf,
#[clap(long, default_value = "/etc/qssh/authorized_keys")]
authorized_keys: PathBuf,
#[clap(long, default_value = "100")]
max_connections: usize,
#[clap(long)]
qkd: bool,
#[clap(long)]
qkd_config: Option<PathBuf>,
#[clap(short, long)]
verbose: bool,
#[clap(short, long)]
daemon: bool,
}
#[tokio::main]
async fn main() {
let args = Args::parse();
if args.verbose {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug")).init();
} else {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
}
if args.generate_keys {
if let Err(e) = generate_host_keys(&args.host_key).await {
eprintln!("Failed to generate host keys: {}", e);
process::exit(1);
}
println!("Host keys generated successfully at: {:?}", args.host_key);
return;
}
let mut config = match QsshServerConfig::new(&args.listen) {
Ok(config) => config,
Err(e) => {
eprintln!("Failed to create server config: {}", e);
process::exit(1);
}
};
config.max_connections = args.max_connections;
if args.authorized_keys.exists() {
match load_authorized_keys(&args.authorized_keys).await {
Ok(keys) => {
for (username, pubkey) in keys {
config.add_authorized_key(&username, pubkey);
}
log::info!("Loaded {} authorized keys", config.authorized_keys.len());
}
Err(e) => {
eprintln!("Failed to load authorized keys: {}", e);
process::exit(1);
}
}
} else {
log::warn!("No authorized_keys file found at {:?}", args.authorized_keys);
}
if args.qkd {
log::info!("QKD support enabled");
config.qkd_enabled = true;
config.qkd_endpoint = Some("https://192.168.0.4/api/v1".to_string());
if let Some(qkd_config) = args.qkd_config {
log::info!("Loading QKD configuration from {:?}", qkd_config);
}
}
if args.daemon {
log::info!("Daemonizing...");
}
let server = QsshServer::new(config);
log::info!("Starting QSSH server on {}", args.listen);
log::info!("Post-quantum algorithms: SPHINCS+, Falcon");
if args.qkd {
log::info!("QKD: Enabled");
} else {
log::info!("QKD: Disabled");
}
match server.start().await {
Ok(()) => {
log::info!("Server stopped normally");
}
Err(e) => {
eprintln!("Server error: {}", e);
process::exit(1);
}
}
}
async fn generate_host_keys(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
use qssh::crypto::PqKeyExchange;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).await?;
}
let _host_key = PqKeyExchange::new()?;
fs::write(path, b"# QSSH Host Key\n# Format: SPHINCS+ and Falcon keys\n").await?;
Ok(())
}
async fn load_authorized_keys(path: &PathBuf) -> Result<Vec<(String, Vec<u8>)>, Box<dyn std::error::Error>> {
let contents = fs::read_to_string(path).await?;
let mut keys = Vec::new();
for line in contents.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
if parts[0].starts_with("qssh-") {
let algorithm = parts[0];
let pubkey_b64 = parts[1];
let comment = if parts.len() > 2 {
parts[2..].join(" ")
} else {
String::new()
};
let username = comment.split('@').next().unwrap_or("*");
if let Ok(pubkey) = base64::engine::general_purpose::STANDARD.decode(pubkey_b64) {
keys.push((username.to_string(), pubkey));
log::info!("Loaded key for user '{}' ({})", username, algorithm);
} else {
log::warn!("Invalid public key for {}", algorithm);
}
} else if parts.len() >= 3 {
let username = parts[0];
let _algorithm = parts[1];
let pubkey_b64 = parts[2];
if let Ok(pubkey) = base64::engine::general_purpose::STANDARD.decode(pubkey_b64) {
keys.push((username.to_string(), pubkey));
log::info!("Loaded key for user '{}'", username);
} else {
log::warn!("Invalid public key for user {}", username);
}
}
}
}
Ok(keys)
}