From 87a40f636d6ad20dd177c0f22c7cd3d13a354fa5 Mon Sep 17 00:00:00 2001 From: ryan Date: Sun, 7 Jan 2024 03:20:17 -0500 Subject: [PATCH] keyfork: impl `derive openpgp` --- Cargo.lock | 3 ++ keyfork-derive-openpgp/src/lib.rs | 1 + keyfork-derive-util/src/path.rs | 9 ++++ keyfork/Cargo.toml | 3 ++ keyfork/src/cli/derive.rs | 76 +++++++++++++++++++++++++++++++ keyfork/src/cli/mod.rs | 7 +++ 6 files changed, 99 insertions(+) create mode 100644 keyfork/src/cli/derive.rs diff --git a/Cargo.lock b/Cargo.lock index 3432919..1c6a000 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1062,10 +1062,13 @@ name = "keyfork" version = "0.1.0" dependencies = [ "clap", + "keyfork-derive-openpgp", + "keyfork-derive-util", "keyfork-mnemonic-util", "keyfork-plumbing", "keyfork-shard", "keyforkd", + "keyforkd-client", "serde", "smex", "thiserror", diff --git a/keyfork-derive-openpgp/src/lib.rs b/keyfork-derive-openpgp/src/lib.rs index bc19120..c794451 100644 --- a/keyfork-derive-openpgp/src/lib.rs +++ b/keyfork-derive-openpgp/src/lib.rs @@ -15,6 +15,7 @@ use sequoia_openpgp::{ types::{KeyFlags, SignatureType}, Cert, Packet, }; +pub use sequoia_openpgp as openpgp; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/keyfork-derive-util/src/path.rs b/keyfork-derive-util/src/path.rs index dfad0cc..eb2469c 100644 --- a/keyfork-derive-util/src/path.rs +++ b/keyfork-derive-util/src/path.rs @@ -42,6 +42,15 @@ impl DerivationPath { pub fn is_empty(&self) -> bool { self.path.is_empty() } + + pub fn push(&mut self, index: DerivationIndex) { + self.path.push(index); + } + + pub fn chain_push(mut self, index: DerivationIndex) -> Self { + self.path.push(index); + self + } } impl std::str::FromStr for DerivationPath { diff --git a/keyfork/Cargo.toml b/keyfork/Cargo.toml index 7335cc2..3074d1c 100644 --- a/keyfork/Cargo.toml +++ b/keyfork/Cargo.toml @@ -16,3 +16,6 @@ keyfork-shard = { version = "0.1.0", path = "../keyfork-shard" } serde = { version = "1.0.192", features = ["derive"] } keyforkd = { version = "0.1.0", path = "../keyforkd", features = ["tracing"] } tokio = { version = "1.35.1", default-features = false, features = ["rt-multi-thread"] } +keyfork-derive-openpgp = { version = "0.1.0", path = "../keyfork-derive-openpgp" } +keyforkd-client = { version = "0.1.0", path = "../keyforkd-client", default-features = false, features = ["ed25519"] } +keyfork-derive-util = { version = "0.1.0", path = "../keyfork-derive-util", default-features = false, features = ["ed25519"] } diff --git a/keyfork/src/cli/derive.rs b/keyfork/src/cli/derive.rs new file mode 100644 index 0000000..3970928 --- /dev/null +++ b/keyfork/src/cli/derive.rs @@ -0,0 +1,76 @@ +use super::Keyfork; +use clap::{Parser, Subcommand}; + +use keyfork_derive_util::{ + request::{DerivationAlgorithm, DerivationRequest, DerivationResponse}, + DerivationIndex, DerivationPath, +}; +use keyforkd_client::Client; +use keyfork_derive_openpgp::openpgp::{types::KeyFlags, packet::UserID, armor::{Kind, Writer}, serialize::Marshal}; + +type Result> = std::result::Result; + +#[derive(Subcommand, Clone, Debug)] +pub enum DeriveSubcommands { + /// Derive an OpenPGP certificate. + #[command(name = "openpgp")] + OpenPGP { + /// Default User ID for the certificate. + user_id: String, + }, +} + +impl DeriveSubcommands { + fn handle(&self, account: DerivationIndex) -> Result<()> { + match self { + DeriveSubcommands::OpenPGP { user_id } => { + let mut pgp_u32 = [0u8; 4]; + pgp_u32[1..].copy_from_slice(&"pgp".bytes().collect::>()); + let chain = DerivationIndex::new(u32::from_be_bytes(pgp_u32), true)?; + let path = DerivationPath::default() + .chain_push(chain) + .chain_push(account); + // TODO: should this be customizable? + let subkeys = vec![ + KeyFlags::empty().set_certification(), + KeyFlags::empty().set_signing(), + KeyFlags::empty().set_transport_encryption().set_storage_encryption(), + KeyFlags::empty().set_authentication(), + ]; + let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path); + let derived_data: DerivationResponse = Client::discover_socket()? + .request(&request.into())? + .try_into()?; + let default_userid = UserID::from(user_id.as_str()); + let cert = keyfork_derive_openpgp::derive(derived_data, &subkeys, &default_userid)?; + + let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?; + + for packet in cert.into_packets() { + packet.serialize(&mut w)?; + } + + w.finalize()?; + } + } + + Ok(()) + } +} + +#[derive(Parser, Debug, Clone)] +pub struct Derive { + #[command(subcommand)] + command: DeriveSubcommands, + + /// Account ID. Required for all derivations. + #[arg(long, global = true, default_value = "0")] + account_id: u32, +} + +impl Derive { + pub fn handle(&self, _k: &Keyfork) -> Result<()> { + let account = DerivationIndex::new(self.account_id, true)?; + self.command.handle(account) + } +} diff --git a/keyfork/src/cli/mod.rs b/keyfork/src/cli/mod.rs index 48516ac..67dfcc0 100644 --- a/keyfork/src/cli/mod.rs +++ b/keyfork/src/cli/mod.rs @@ -1,5 +1,6 @@ use clap::{Parser, Subcommand}; +mod derive; mod mnemonic; mod provision; mod shard; @@ -16,6 +17,9 @@ pub struct Keyfork { #[derive(Subcommand, Clone, Debug)] pub enum KeyforkCommands { + /// Derive keys of various formats. + Derive(derive::Derive), + /// Mnemonic generation and persistence utilities. Mnemonic(mnemonic::Mnemonic), @@ -33,6 +37,9 @@ pub enum KeyforkCommands { impl KeyforkCommands { pub fn handle(&self, keyfork: &Keyfork) -> Result<(), Box> { match self { + KeyforkCommands::Derive(d) => { + d.handle(keyfork)?; + } KeyforkCommands::Mnemonic(m) => { let response = m.command.handle(m, keyfork)?; println!("{response}");