/// Signs data using the openpgp crate and secrets in gpg-agent. use std::io; use std::path::PathBuf; use clap::CommandFactory; use clap::FromArgMatches; use clap::Parser; use sequoia_openpgp as openpgp; use openpgp::parse::Parse; use openpgp::serialize::stream::{Armorer, Message, LiteralWriter, Signer}; use openpgp::policy::StandardPolicy as P; use sequoia_gpg_agent as gpg_agent; use gpg_agent::Context; use gpg_agent::KeyPair; /// Defines the CLI. #[derive(Parser, Debug)] #[clap( name = "gpg-agent-sign", about = "Connects to gpg-agent and creates a dummy signature.", )] pub struct Cli { #[clap( long, value_name = "PATH", env = "GNUPGHOME", help = "Use this GnuPG home directory, default: $GNUPGHOME", )] homedir: Option, #[clap( long, value_name = "CERT", help = "Public part of the secret keys managed by gpg-agent", required = true, )] cert: Vec, } fn main() -> openpgp::Result<()> { let p = &P::new(); let version = format!( "{} (sequoia-openpgp {}, using {})", env!("CARGO_PKG_VERSION"), sequoia_openpgp::VERSION, sequoia_openpgp::crypto::backend()); let cli = Cli::command().version(version); let matches = Cli::from_arg_matches(&cli.get_matches())?; let ctx = if let Some(homedir) = matches.homedir { Context::with_homedir(homedir)? } else { Context::new()? }; // Read the Certs from the given files. let certs = matches.cert.into_iter().map( openpgp::Cert::from_file ).collect::, _>>()?; // Construct a KeyPair for every signing-capable (sub)key. use openpgp::cert::amalgamation::ValidAmalgamation; let mut signers = certs.iter().flat_map(|cert| { cert.keys().with_policy(p, None).alive().revoked(false).for_signing() .filter_map(|ka| { KeyPair::new_for_gnupg_context(&ctx, ka.key()) .map(|kp| kp.with_cert(ka.cert())) .ok() }) }).collect::>(); // Compose a writer stack corresponding to the output format and // packet structure we want. // Stream an OpenPGP message. let message = Message::new(io::stdout()); // We want the output to be ASCII armored. let message = Armorer::new(message).build()?; // Now, create a signer that emits the signature(s). let mut signer = Signer::new(message, signers.pop().expect("No key for signing")); for s in signers { signer = signer.add_signer(s); } let signer = signer.build()?; // Then, create a literal writer to wrap the data in a literal // message packet. let mut literal = LiteralWriter::new(signer).build()?; // Copy all the data. io::copy(&mut io::stdin(), &mut literal)?; // Finally, teardown the stack to ensure all the data is written. literal.finalize()?; Ok(()) }