From 108bd332fabaa05009f1315d925664d2007bcdb2 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 22 Aug 2018 17:06:56 +0200 Subject: openpgp: Implement proper handling of nested signatures. - Outer signatures are over any inner signatures and the intervening content. Since outer signatures are notarizations, they should treat literal data packets the same way normal signatures (i.e., inner most signatures) do and ignore the filename, date, etc. fields when hashing the content. - Add a test vector for this, and an example that creates these kinds of notarizations. - Fixes #29. --- openpgp/examples/notarize.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 openpgp/examples/notarize.rs (limited to 'openpgp/examples') diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs new file mode 100644 index 00000000..d3426131 --- /dev/null +++ b/openpgp/examples/notarize.rs @@ -0,0 +1,98 @@ +/// This program demonstrates how to notarize an OpenPGP message. + +use std::env; +use std::io; + +extern crate openpgp; +use openpgp::{ + armor, + Packet, + constants::DataFormat, + parse::PacketParserResult, + serialize::Serialize, +}; +use openpgp::serialize::stream::{wrap, LiteralWriter, Signer}; + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 2 { + panic!("A simple notarizing filter.\n\n\ + Usage: {} [...] \ + output\n", args[0]); + } + + // Read the transferable secret keys from the given files. + let tsks: Vec = args[1..].iter().map(|f| { + openpgp::TPK::from_reader( + // Use an openpgp::Reader so that we accept both armored + // and plain PGP data. + openpgp::Reader::from_file(f) + .expect("Failed to open file")) + .expect("Failed to read key") + }).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."); + + // Now, create a signer that emits a detached signature. + let mut signer = Signer::new( + wrap(sink), &tsks.iter().collect::>()) + .expect("Failed to create signer"); + + // Create a parser for the message to be notarized. + let mut input = io::stdin(); + let mut ppr + = openpgp::parse::PacketParser::from_reader( + openpgp::Reader::from_reader(&mut input) + .expect("Failed to build reader")) + .expect("Failed to build parser"); + + while let PacketParserResult::Some(mut pp) = ppr { + if ! pp.possible_message() { + panic!("Malformed OpenPGP message"); + } + + match pp.packet { + Packet::PKESK(_) | Packet::SKESK(_) => + panic!("Encrypted messages are not supported"), + Packet::OnePassSig(ref ops) => + ops.serialize(&mut signer).expect("Failed to serialize"), + Packet::Literal(_) => { + // 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"); + + // Finally, just copy all the data. + io::copy(&mut pp, &mut literal) + .expect("Failed to sign data"); + + signer = literal.finalize_one() + .expect("Failed to sign data") + .unwrap(); + }, + Packet::Signature(ref sig) => + sig.serialize(&mut signer).expect("Failed to serialize"), + _ => (), + } + + let (_, _, ppr_tmp, _) = pp.recurse() + .expect("Failed to recurse"); + ppr = ppr_tmp; + } + if let PacketParserResult::EOF(eof) = ppr { + if ! eof.is_message() { + panic!("Malformed OpenPGP message") + } + } else { + unreachable!() + } + + // Teardown the stack to ensure all the data is written. + signer.finalize() + .expect("Failed to write data"); +} -- cgit v1.2.3