keyfork wizard generate-shard-secret: allow exporting certificates and cross-sign generated keys

This commit is contained in:
Ryan Heywood 2025-01-24 08:06:40 -05:00
parent c95ed0b729
commit 8756c3d233
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
1 changed files with 55 additions and 4 deletions

View File

@ -9,9 +9,10 @@ use keyfork_derive_openpgp::{
openpgp::{ openpgp::{
self, self,
armor::{Kind, Writer}, armor::{Kind, Writer},
packet::UserID, packet::{UserID, signature::SignatureBuilder},
serialize::Marshal, serialize::Marshal,
types::KeyFlags, types::{SignatureType, KeyFlags},
policy::StandardPolicy,
Cert, Cert,
}, },
XPrv, XPrv,
@ -20,8 +21,7 @@ use keyfork_derive_path_data::paths;
use keyfork_derive_util::DerivationIndex; use keyfork_derive_util::DerivationIndex;
use keyfork_mnemonic::Mnemonic; use keyfork_mnemonic::Mnemonic;
use keyfork_prompt::{ use keyfork_prompt::{
default_handler, default_handler, prompt_validated_passphrase,
prompt_validated_passphrase,
validators::{SecurePinValidator, Validator}, validators::{SecurePinValidator, Validator},
Message, Message,
}; };
@ -133,6 +133,10 @@ pub struct GenerateShardSecret {
/// The file to write the generated shard file to. /// The file to write the generated shard file to.
#[arg(long)] #[arg(long)]
output: Option<PathBuf>, output: Option<PathBuf>,
/// The file to write generated certificates to.
#[arg(long)]
cert_output: Option<PathBuf>,
} }
/// Create a 256 bit secret and shard the secret to previously known OpenPGP certificates, /// 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<dyn std::error::Error>> {
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 { impl GenerateShardSecret {
fn handle(&self) -> Result<()> { fn handle(&self) -> Result<()> {
let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?; let seed = keyfork_entropy::generate_entropy_of_const_size::<{ 256 / 8 }>()?;
@ -237,6 +276,8 @@ impl GenerateShardSecret {
certs.push(cert); certs.push(cert);
} }
cross_sign_certs(&mut certs)?;
let opgp = OpenPGP; let opgp = OpenPGP;
if let Some(output_file) = self.output.as_ref() { if let Some(output_file) = self.output.as_ref() {
@ -251,6 +292,16 @@ impl GenerateShardSecret {
std::io::stdout(), 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(()) Ok(())
} }
} }