diff --git a/keyfork/Cargo.toml b/keyfork/Cargo.toml index 73dc447..7335cc2 100644 --- a/keyfork/Cargo.toml +++ b/keyfork/Cargo.toml @@ -14,5 +14,5 @@ smex = { version = "0.1.0", path = "../smex" } keyfork-plumbing = { version = "0.1.0", path = "../keyfork-plumbing" } keyfork-shard = { version = "0.1.0", path = "../keyfork-shard" } serde = { version = "1.0.192", features = ["derive"] } -keyforkd = { version = "0.1.0", path = "../keyforkd" } +keyforkd = { version = "0.1.0", path = "../keyforkd", features = ["tracing"] } tokio = { version = "1.35.1", default-features = false, features = ["rt-multi-thread"] } diff --git a/keyfork/src/cli/recover.rs b/keyfork/src/cli/recover.rs new file mode 100644 index 0000000..474faca --- /dev/null +++ b/keyfork/src/cli/recover.rs @@ -0,0 +1,109 @@ +use super::Keyfork; +use clap::{builder::PossibleValue, Parser, Subcommand, ValueEnum}; +use std::path::PathBuf; + +use keyfork_mnemonic_util::Mnemonic; +use keyfork_shard::openpgp::{combine, discover_certs, parse_messages}; + +type Result> = std::result::Result; + +trait IntoSeed { + fn retrieve_seed(&self) -> Result>; +} + +#[derive(Clone, Debug)] +pub struct Shard; + +impl IntoSeed for Shard { + fn retrieve_seed(&self) -> Result> { + todo!() + } +} + +#[derive(Clone, Debug)] +pub struct RemoteShard; + +impl IntoSeed for RemoteShard { + fn retrieve_seed(&self) -> Result> { + todo!() + } +} + +#[derive(Clone, Debug)] +pub enum SeedFormat { + Shard(Shard), + RemoteShard(RemoteShard), +} + +impl ValueEnum for SeedFormat { + fn value_variants<'a>() -> &'a [Self] { + &[Self::Shard(Shard), Self::RemoteShard(RemoteShard)] + } + + fn to_possible_value(&self) -> Option { + Some(match self { + SeedFormat::Shard(_) => PossibleValue::new("shard"), + SeedFormat::RemoteShard(_) => PossibleValue::new("remote-shard"), + }) + } +} + +#[derive(Subcommand, Clone, Debug)] +pub enum RecoverSubcommands { + /// Decrypt a shard file using keys available on the local system. + Shard { + shard_file: PathBuf, + key_discovery: Option, + }, + + /// Combine remotely decrypted shards. + RemoteShard {}, +} + +impl RecoverSubcommands { + fn handle(&self) -> Result> { + match self { + RecoverSubcommands::Shard { + shard_file, + key_discovery, + } => { + let content = std::fs::read_to_string(shard_file)?; + if content.contains("BEGIN PGP MESSAGE") { + let certs = key_discovery + .as_ref() + .map(discover_certs) + .transpose()? + .unwrap_or(vec![]); + let mut messages = parse_messages(content.as_bytes())?; + let metadata = messages.pop_front().expect("any pgp encrypted message"); + let mut seed = vec![]; + combine(certs, &metadata, messages.into(), &mut seed)?; + return Ok(seed); + } + } + RecoverSubcommands::RemoteShard {} => todo!(), + } + todo!() + } +} + +#[derive(Parser, Debug, Clone)] +pub struct Recover { + #[command(subcommand)] + command: RecoverSubcommands, +} + +impl Recover { + pub fn handle(&self, _k: &Keyfork) -> Result<()> { + let seed = self.command.handle()?; + let mnemonic = Mnemonic::from_entropy(&seed, Default::default())?; + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + keyforkd::setup_registry(); + keyforkd::start_and_run_server(mnemonic).await + }) + } +} diff --git a/keyforkd/src/lib.rs b/keyforkd/src/lib.rs index bd2e29a..20d9a62 100644 --- a/keyforkd/src/lib.rs +++ b/keyforkd/src/lib.rs @@ -9,6 +9,14 @@ pub use tower::ServiceBuilder; #[cfg(feature = "tracing")] use tracing::debug; +#[cfg(feature = "tracing")] +use tracing_subscriber::{ + filter::{EnvFilter, LevelFilter}, + fmt::{format::FmtSpan, layer}, + prelude::*, + registry, +}; + pub mod error; pub mod middleware; pub mod server; @@ -17,6 +25,18 @@ pub use error::Keyforkd as KeyforkdError; pub use server::UnixServer; pub use service::Keyforkd; +#[cfg(feature = "tracing")] +pub fn setup_registry() { + let envfilter = EnvFilter::builder() + .with_default_directive(LevelFilter::DEBUG.into()) + .from_env_lossy(); + registry() + .with(envfilter) + .with(layer().with_span_events(FmtSpan::CLOSE)) + .with(tracing_error::ErrorLayer::default()) + .init(); +} + pub async fn start_and_run_server_on( mnemonic: Mnemonic, socket_path: &Path, diff --git a/keyforkd/src/main.rs b/keyforkd/src/main.rs index eb333e3..dfe8853 100644 --- a/keyforkd/src/main.rs +++ b/keyforkd/src/main.rs @@ -4,13 +4,6 @@ use tokio::io::{self, AsyncBufReadExt, BufReader}; #[cfg(feature = "tracing")] use tracing::debug; -#[cfg(feature = "tracing")] -use tracing_subscriber::{ - filter::{EnvFilter, LevelFilter}, - fmt::{format::FmtSpan, layer}, - prelude::*, - registry, -}; type Result> = std::result::Result; @@ -21,23 +14,11 @@ async fn load_mnemonic() -> Result { Ok(line.parse()?) } -#[cfg(feature = "tracing")] -fn setup_registry() { - let envfilter = EnvFilter::builder() - .with_default_directive(LevelFilter::DEBUG.into()) - .from_env_lossy(); - registry() - .with(envfilter) - .with(layer().with_span_events(FmtSpan::CLOSE)) - .with(tracing_error::ErrorLayer::default()) - .init(); -} - #[cfg_attr(feature = "multithread", tokio::main)] #[cfg_attr(not(feature = "multithread"), tokio::main(flavor = "current_thread"))] async fn main() -> Result<(), Box> { #[cfg(feature = "tracing")] - setup_registry(); + keyforkd::setup_registry(); #[cfg(feature = "tracing")] debug!("reading mnemonic from standard input");