use std::env::set_var;
use std::fs::create_dir_all;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::process::exit;
use clap::Parser;
use typhoon::certificate::ServerKeyPair;
#[derive(Parser)]
#[command(name = "typhoon-gen-key", version)]
struct Args {
server_key: PathBuf,
#[arg(long, value_name = "PATH")]
cert: Option<PathBuf>,
#[arg(long, value_name = "IP:PORT")]
addr: Vec<SocketAddr>,
#[arg(long = "set", value_name = "KEY=VALUE")]
overrides: Vec<String>,
}
fn main() {
let args = Args::parse();
if args.cert.is_some() && args.addr.is_empty() {
eprintln!("error: --cert requires at least one --addr");
exit(1);
}
for kv in &args.overrides {
let (key, value) = kv.split_once('=').unwrap_or_else(|| {
eprintln!("error: --set argument must be KEY=VALUE, got '{kv}'");
exit(1);
});
unsafe {
set_var(key, value);
}
}
println!("Generating server key pair (this may take a few seconds)...");
let key_pair = ServerKeyPair::generate();
if let Some(parent) = Path::new(&args.server_key).parent() {
create_dir_all(parent).unwrap_or_else(|e| {
eprintln!("Failed to create directories for '{}': {e}", args.server_key.display());
exit(1);
});
}
key_pair.save(&args.server_key).unwrap_or_else(|e| {
eprintln!("Failed to save server key to '{}': {e}", args.server_key.display());
exit(1);
});
println!("Server key pair written to: {}", args.server_key.display());
if let Some(ref cert_out) = args.cert {
if let Some(parent) = Path::new(cert_out).parent() {
create_dir_all(parent).unwrap_or_else(|e| {
eprintln!("Failed to create directories for '{}': {e}", cert_out.display());
exit(1);
});
}
let cert = key_pair.to_client_certificate(args.addr.clone());
cert.save(cert_out).unwrap_or_else(|e| {
eprintln!("Failed to save client certificate to '{}': {e}", cert_out.display());
exit(1);
});
println!("Client certificate written to: {}", cert_out.display());
println!(" Embedded addresses:");
for addr in &args.addr {
println!(" {addr}");
}
}
}