keyfork: impl `derive openpgp`

This commit is contained in:
Ryan Heywood 2024-01-07 03:20:17 -05:00
parent 6fc2c47391
commit 87a40f636d
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
6 changed files with 99 additions and 0 deletions

3
Cargo.lock generated
View File

@ -1062,10 +1062,13 @@ name = "keyfork"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"keyfork-derive-openpgp",
"keyfork-derive-util",
"keyfork-mnemonic-util", "keyfork-mnemonic-util",
"keyfork-plumbing", "keyfork-plumbing",
"keyfork-shard", "keyfork-shard",
"keyforkd", "keyforkd",
"keyforkd-client",
"serde", "serde",
"smex", "smex",
"thiserror", "thiserror",

View File

@ -15,6 +15,7 @@ use sequoia_openpgp::{
types::{KeyFlags, SignatureType}, types::{KeyFlags, SignatureType},
Cert, Packet, Cert, Packet,
}; };
pub use sequoia_openpgp as openpgp;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {

View File

@ -42,6 +42,15 @@ impl DerivationPath {
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.path.is_empty() 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 { impl std::str::FromStr for DerivationPath {

View File

@ -16,3 +16,6 @@ keyfork-shard = { version = "0.1.0", path = "../keyfork-shard" }
serde = { version = "1.0.192", features = ["derive"] } serde = { version = "1.0.192", features = ["derive"] }
keyforkd = { version = "0.1.0", path = "../keyforkd", features = ["tracing"] } keyforkd = { version = "0.1.0", path = "../keyforkd", features = ["tracing"] }
tokio = { version = "1.35.1", default-features = false, features = ["rt-multi-thread"] } 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"] }

76
keyfork/src/cli/derive.rs Normal file
View File

@ -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<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
#[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::<Vec<u8>>());
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)
}
}

View File

@ -1,5 +1,6 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
mod derive;
mod mnemonic; mod mnemonic;
mod provision; mod provision;
mod shard; mod shard;
@ -16,6 +17,9 @@ pub struct Keyfork {
#[derive(Subcommand, Clone, Debug)] #[derive(Subcommand, Clone, Debug)]
pub enum KeyforkCommands { pub enum KeyforkCommands {
/// Derive keys of various formats.
Derive(derive::Derive),
/// Mnemonic generation and persistence utilities. /// Mnemonic generation and persistence utilities.
Mnemonic(mnemonic::Mnemonic), Mnemonic(mnemonic::Mnemonic),
@ -33,6 +37,9 @@ pub enum KeyforkCommands {
impl KeyforkCommands { impl KeyforkCommands {
pub fn handle(&self, keyfork: &Keyfork) -> Result<(), Box<dyn std::error::Error>> { pub fn handle(&self, keyfork: &Keyfork) -> Result<(), Box<dyn std::error::Error>> {
match self { match self {
KeyforkCommands::Derive(d) => {
d.handle(keyfork)?;
}
KeyforkCommands::Mnemonic(m) => { KeyforkCommands::Mnemonic(m) => {
let response = m.command.handle(m, keyfork)?; let response = m.command.handle(m, keyfork)?;
println!("{response}"); println!("{response}");