summaryrefslogtreecommitdiffstats
path: root/openpgp/examples
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-08-22 17:06:56 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-08-22 17:10:49 +0200
commit108bd332fabaa05009f1315d925664d2007bcdb2 (patch)
treeeb7f175f84dc8b2592f3d3036f2a824c8b12edea /openpgp/examples
parentc2fe7c5dcfb0f268ec16b3996d85b35a9c54eb47 (diff)
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.
Diffstat (limited to 'openpgp/examples')
-rw-r--r--openpgp/examples/notarize.rs98
1 files changed, 98 insertions, 0 deletions
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<String> = env::args().collect();
+ if args.len() < 2 {
+ panic!("A simple notarizing filter.\n\n\
+ Usage: {} <secret-keyfile> [<secret-keyfile>...] \
+ <input >output\n", args[0]);
+ }
+
+ // Read the transferable secret keys from the given files.
+ let tsks: Vec<openpgp::TPK> = 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::<Vec<&openpgp::TPK>>())
+ .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");
+}