From 8756c3d233ebe9ef4349eec1e690dd5240e15bda Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 24 Jan 2025 08:06:40 -0500 Subject: [PATCH] keyfork wizard generate-shard-secret: allow exporting certificates and cross-sign generated keys --- crates/keyfork/src/cli/wizard.rs | 59 +++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/crates/keyfork/src/cli/wizard.rs b/crates/keyfork/src/cli/wizard.rs index d2b7be3..46c83f1 100644 --- a/crates/keyfork/src/cli/wizard.rs +++ b/crates/keyfork/src/cli/wizard.rs @@ -9,9 +9,10 @@ use keyfork_derive_openpgp::{ openpgp::{ self, armor::{Kind, Writer}, - packet::UserID, + packet::{UserID, signature::SignatureBuilder}, serialize::Marshal, - types::KeyFlags, + types::{SignatureType, KeyFlags}, + policy::StandardPolicy, Cert, }, XPrv, @@ -20,8 +21,7 @@ use keyfork_derive_path_data::paths; use keyfork_derive_util::DerivationIndex; use keyfork_mnemonic::Mnemonic; use keyfork_prompt::{ - default_handler, - prompt_validated_passphrase, + default_handler, prompt_validated_passphrase, validators::{SecurePinValidator, Validator}, Message, }; @@ -133,6 +133,10 @@ pub struct GenerateShardSecret { /// The file to write the generated shard file to. #[arg(long)] output: Option, + + /// The file to write generated certificates to. + #[arg(long)] + cert_output: Option, } /// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates, @@ -172,6 +176,41 @@ impl WizardSubcommands { } } +fn cross_sign_certs(certs: &mut [Cert]) -> Result<(), Box> { + let policy = StandardPolicy::new(); + for signing_cert in certs.to_vec() { + let mut certify_key = signing_cert + .with_policy(&policy, None)? + .keys() + .unencrypted_secret() + .for_certification() + .next() + .expect("certify key unusable/not found") + .key() + .clone() + .into_keypair()?; + for signable_cert in certs.iter_mut() { + let sb = SignatureBuilder::new(SignatureType::GenericCertification); + let userid = signable_cert + .userids() + .next() + .expect("a signable user ID is necessary to create web of trust"); + let signature = sb.sign_userid_binding( + &mut certify_key, + signable_cert.primary_key().key(), + &*userid, + )?; + let changed; + (*signable_cert, changed) = signable_cert.clone().insert_packets2(signature)?; + assert!( + changed, + "OpenPGP certificate was unchanged after inserting packets" + ); + } + } + Ok(()) +} + impl GenerateShardSecret { fn handle(&self) -> Result<()> { let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?; @@ -237,6 +276,8 @@ impl GenerateShardSecret { certs.push(cert); } + cross_sign_certs(&mut certs)?; + let opgp = OpenPGP; if let Some(output_file) = self.output.as_ref() { @@ -251,6 +292,16 @@ impl GenerateShardSecret { std::io::stdout(), )?; } + + if let Some(cert_output_file) = self.cert_output.as_ref() { + let output = File::create(cert_output_file)?; + let mut writer = Writer::new(output, Kind::PublicKey)?; + for cert in certs { + cert.serialize(&mut writer)?; + } + writer.finalize()?; + } + Ok(()) } }