use std::{env, process::ExitCode, str::FromStr}; use keyfork_derive_path_data::paths; use keyfork_derive_util::{DerivationPath, ExtendedPrivateKey, PathError}; use keyforkd_client::Client; use ed25519_dalek::SigningKey; type XPrv = ExtendedPrivateKey; /// Any error that can occur while deriving a key. #[derive(Debug, thiserror::Error)] pub enum Error { /// The given path could not be parsed. #[error("Could not parse the given path: {0}")] PathFormat(#[from] PathError), /// The request to derive data failed. #[error("Unable to perform key derivation request: {0}")] KeyforkdClient(#[from] keyforkd_client::Error), } #[allow(missing_docs)] pub type Result = std::result::Result; fn validate(path: &str) -> Result { let index = paths::AGE.inner().first().unwrap(); let path = DerivationPath::from_str(path)?; assert!( path.len() >= 2, "Expected path of at least m/{index}/account_id'" ); let given_index = path.iter().next().expect("checked .len() above"); assert_eq!( index, given_index, "Expected derivation path starting with m/{index}, got: {given_index}", ); Ok(path) } fn run() -> Result<(), Box> { let mut args = env::args(); let program_name = args.next().expect("program name"); let args = args.collect::>(); let path = match args.as_slice() { [path] => validate(path)?, _ => panic!("Usage: {program_name} path"), }; let mut client = Client::discover_socket()?; // TODO: should this key be clamped to Curve25519 specs? let xprv: XPrv = client.request_xprv(&path)?; let hrp = bech32::Hrp::parse("AGE-SECRET-KEY-")?; let age_key = bech32::encode::(hrp, &xprv.private_key().to_bytes())?; println!("{}", age_key.to_uppercase()); Ok(()) } fn main() -> ExitCode { if let Err(e) = run() { eprintln!("Error: {e}"); ExitCode::FAILURE } else { ExitCode::SUCCESS } }