From 433437b20b8cb33263a293f34589e29eeacabfca Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Sat, 8 Jun 2019 14:47:27 +0200 Subject: ipc: GnuPG RPC support. - This allows us to communicate with gpg-agent, and use it for cryptographic operations. --- ipc/examples/gpg-agent-sign.rs | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 ipc/examples/gpg-agent-sign.rs (limited to 'ipc/examples/gpg-agent-sign.rs') diff --git a/ipc/examples/gpg-agent-sign.rs b/ipc/examples/gpg-agent-sign.rs new file mode 100644 index 00000000..8525abbb --- /dev/null +++ b/ipc/examples/gpg-agent-sign.rs @@ -0,0 +1,76 @@ +/// Signs data using the openpgp crate and secrets in gpg-agent. + +use std::io; + +extern crate clap; +extern crate sequoia_openpgp as openpgp; +extern crate sequoia_ipc as ipc; + +use openpgp::armor; +use openpgp::constants::DataFormat; +use openpgp::parse::Parse; +use openpgp::serialize::stream::{Message, LiteralWriter, Signer}; +use ipc::gnupg::{Context, KeyPair}; + +fn main() { + let matches = clap::App::new("gpg-agent-sign") + .version(env!("CARGO_PKG_VERSION")) + .about("Connects to gpg-agent and creates a dummy signature.") + .arg(clap::Arg::with_name("homedir").value_name("PATH") + .long("homedir") + .help("Use this GnuPG home directory, default: $GNUPGHOME")) + .arg(clap::Arg::with_name("tpk").value_name("TPK") + .required(true) + .multiple(true) + .help("Public part of the secret keys managed by gpg-agent")) + .get_matches(); + + let ctx = if let Some(homedir) = matches.value_of("homedir") { + Context::with_homedir(homedir).unwrap() + } else { + Context::new().unwrap() + }; + + // Read the TPKs from the given files. + let tpks = + matches.values_of("tpk").expect("required").map(|f| { + openpgp::TPK::from_file(f) + .expect("Failed to read key") + }).collect::>(); + + // Construct a KeyPair for every signing-capable (sub)key. + let mut keypairs = tpks.iter().flat_map(|tpk| tpk.keys_valid().signing_capable().filter_map(|(_, _, key)| { + KeyPair::new(&ctx, key).ok() + })).collect::>(); + + // Well, this is awkward... + let signers = keypairs.iter_mut() + .map(|s| -> &mut dyn openpgp::crypto::Signer { s }) + .collect(); + + // Compose a writer stack corresponding to the output format and + // packet structure we want. First, we want the output to be + // ASCII armored. + let sink = armor::Writer::new(io::stdout(), armor::Kind::Message, &[]) + .expect("Failed to create an armored writer."); + + // Stream an OpenPGP message. + let message = Message::new(sink); + + // Now, create a signer that emits a signature. + let signer = Signer::new(message, signers, None) + .expect("Failed to create signer"); + + // Then, create a literal writer to wrap the data in a literal + // message packet. + let mut literal = LiteralWriter::new(signer, DataFormat::Binary, None, None) + .expect("Failed to create literal writer"); + + // Copy all the data. + io::copy(&mut io::stdin(), &mut literal) + .expect("Failed to sign data"); + + // Finally, teardown the stack to ensure all the data is written. + literal.finalize() + .expect("Failed to write data"); +} -- cgit v1.2.3