summaryrefslogtreecommitdiffstats
path: root/guide/src/chapter_03.md
diff options
context:
space:
mode:
Diffstat (limited to 'guide/src/chapter_03.md')
-rw-r--r--guide/src/chapter_03.md221
1 files changed, 221 insertions, 0 deletions
diff --git a/guide/src/chapter_03.md b/guide/src/chapter_03.md
new file mode 100644
index 00000000..80d5cc1c
--- /dev/null
+++ b/guide/src/chapter_03.md
@@ -0,0 +1,221 @@
+Describes how to use some of Sequoia's parsers.
+
+Sequoia contains and exposes several parsers. In this chapter, we
+will cover some of them, starting from a high level parser, the
+[`TPKParser`] that parses transferable public keys ([`TPK`]s), all
+down to the actual OpenPGP [`PacketParser`].
+
+[`TPKParser`]: ../../sequoia_openpgp/tpk/struct.TPKParser.html
+[`TPK`]: ../../sequoia_openpgp/struct.TPK.html
+[`PacketParser`]: ../../sequoia_openpgp/parse/struct.PacketParser.html
+
+# Parsing TPKs
+
+First, we will start with a string that presumably contains a
+transferable public key, and feed it into the [`TPKParser`]. On
+success, we can use or examine the resulting [`TPK`]:
+
+```rust
+extern crate sequoia_openpgp as openpgp;
+use openpgp::parse::Parse;
+
+const KEY: &str =
+ "-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+ xjMEXAfmvxYJKwYBBAHaRw8BAQdAVNM03IK1KDgDNCbf4XcARhfqzyx425FEJMQ5
+ qF+DrwHNF+G8iM+BzrnPg8+Ezr/PhM6tzrvOt8+CwoQEExYKADYCHgMCmwEFglwH
+ 5r8FiQWfpgAWIQTAh0R4plxUCh9zcrSiLq1hTRF0SgkQoi6tYU0RdEoCFQoAALip
+ AP4sSVgNJogb/v0Qst0+WlmrJ6upG8Ynao5mnRFmfx2LjAEAyGJJBaEBB+x4kOse
+ 9uACwAXFhBRLN9zGgbyySQ3fRwjOMwRcB+a/FgkrBgEEAdpHDwEBB0BXBFWMeVd1
+ nNn/VqTVEgY3wknX/KkKfMWhslFJoyZ4L8LAOAQYFgoAMwKbAgWCXAfmvwWJBZ+m
+ ABYhBMCHRHimXFQKH3NytKIurWFNEXRKCRCiLq1hTRF0SgIVCgB3dqAEGRYKACcF
+ glwH5r8WIQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAMuvAQDdRfbM
+ u2bDtVqNLIP/0WD/5X0us49r1yXMH+Ilg5NEEQEAuSQ1pY+reS62ETUS0uKYhxxv
+ 7OOsr8YM/ZMQ0exZsw/u+QEAuakAXrR7uFmWyigopQ7qMYfnK5zNfQNykvony5tS
+ HpEBAJs3ZwHq+Q0ziAZNgcvdp0mklx8IXd8x59NjiP1t3mUBzjgEXAfmvxIKKwYB
+ BAGXVQEFAQEHQJuIvcDm3Sh0+ZOE5hj7jCBas2xOCqYiG6+bWWieoxRrAwEICcKB
+ BBgWCgAzApsMBYJcB+a/BYkFn6YAFiEEwIdEeKZcVAofc3K0oi6tYU0RdEoJEKIu
+ rWFNEXRKAgsJAADx4wD/VrXZ7I/hBC37lzhyVEcCaHcorVXVn8ACCiyRmgmNbY4A
+ /1lJmQJoDlpYlx3BAJ6RYuXRJoyU5KpcBf5afBPn8ncB
+ =MHBq
+ -----END PGP PUBLIC KEY BLOCK-----";
+
+fn main() {
+ let tpk = openpgp::TPK::from_bytes(KEY.as_bytes()).unwrap();
+
+ assert_eq!(tpk.fingerprint().to_string(),
+ "C087 4478 A65C 540A 1F73 72B4 A22E AD61 4D11 744A");
+
+ // Iterate over UserIDs.
+ assert_eq!(tpk.userids().count(), 1);
+ assert_eq!(tpk.userids().nth(0).unwrap().userid(),
+ &"Ἀριστοτέλης".into());
+
+ // Iterate over subkeys.
+ assert_eq!(tpk.subkeys().count(), 2);
+ assert_eq!(tpk.subkeys().nth(0).unwrap().subkey().fingerprint().to_string(),
+ "67A4 8753 A380 A6B3 B7DF 7DC5 E6C6 897A 4CEF 8924");
+ assert_eq!(tpk.subkeys().nth(1).unwrap().subkey().fingerprint().to_string(),
+ "185C DAA1 2723 0423 19E4 7F67 108F 2CAF 9034 356D");
+}
+```
+
+# Parsing OpenPGP messages
+
+Not all sequences of OpenPGP packets are in valid OpenPGP
+[`Message`]s, only those accepted by [this grammar] are. Sequoia
+contains a parser that parses packets and verifies the message
+structure using this grammar:
+
+[this grammar]: https://tools.ietf.org/html/rfc4880#section-11.3
+[`Message`]: ../../sequoia_openpgp/struct.Message.html
+
+```rust
+extern crate sequoia_openpgp as openpgp;
+use openpgp::parse::Parse;
+
+const MESSAGE: &str =
+ "-----BEGIN PGP MESSAGE-----
+
+ xA0DAAoW5saJekzviSQByxBiAAAAAADYtdiv2KfZgtipwnUEABYKACcFglwJHYoW
+ IQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAIJ6APwK6FxtHXn8txDl
+ tBFsIXlOSLOs4BvArlZzZSMomIyFLAEAwCLJUChMICDxWXRlHxORqU5x6hlO3DdW
+ sl/1DAbnRgI=
+ =AqoO
+ -----END PGP MESSAGE-----";
+
+fn main() {
+ let message = openpgp::Message::from_bytes(MESSAGE.as_bytes()).unwrap();
+
+ assert_eq!(message.body().and_then(|literal| literal.body()),
+ Some("صداقة".as_bytes()));
+}
+```
+
+# Parsing packets into packet piles
+
+[`PacketPile`]s are unstructured sequences of OpenPGP packets. Packet
+piles can be inspected, manipulated, validated using a formal grammar
+and thereby turned into [`Message`]s or [`TPK`]s using
+[`Message::from_packet_pile`] or [`TPK::from_packet_pile`], or just
+turned into a vector of [`Packet`]s:
+
+[`PacketPile`]: ../../sequoia_openpgp/struct.PacketPile.html
+[`Packet`]: ../../sequoia_openpgp/enum.Packet.html
+[`TPK::from_packet_pile`]: ../../sequoia_openpgp/struct.TPK.html#method.from_packet_pile
+[`Message::from_packet_pile`]: ../../sequoia_openpgp/struct.Message.html#method.from_packet_pile
+
+```rust
+extern crate sequoia_openpgp as openpgp;
+use openpgp::parse::Parse;
+
+const MESSAGE: &str =
+ "-----BEGIN PGP MESSAGE-----
+
+ xA0DAAoW5saJekzviSQByxBiAAAAAADYtdiv2KfZgtipwnUEABYKACcFglwJHYoW
+ IQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAIJ6APwK6FxtHXn8txDl
+ tBFsIXlOSLOs4BvArlZzZSMomIyFLAEAwCLJUChMICDxWXRlHxORqU5x6hlO3DdW
+ sl/1DAbnRgI=
+ =AqoO
+ -----END PGP MESSAGE-----";
+
+fn main() {
+ let pile = openpgp::PacketPile::from_bytes(MESSAGE.as_bytes()).unwrap();
+
+ // For simplicity, turn the pile into a vector of packets.
+ let packets: Vec<openpgp::Packet> = pile.into_children().collect();
+
+ // There are three packets in that message.
+ assert_eq!(packets.len(), 3);
+
+ // First, we expect an one pass signature packet.
+ if let openpgp::Packet::OnePassSig(ref ops) = packets[0] {
+ assert_eq!(ops.issuer().to_string(), "E6C6 897A 4CEF 8924");
+ } else {
+ panic!("expected one pass signature packet");
+ }
+
+ // The second packet is the literal data packet.
+ if let openpgp::Packet::Literal(ref literal) = packets[1] {
+ assert_eq!(literal.body(), Some("صداقة".as_bytes()));
+ } else {
+ panic!("expected literal data packet");
+ }
+
+ // Finally, we expect the signature itself.
+ if let openpgp::Packet::Signature(ref signature) = packets[2] {
+ assert_eq!(signature.issuer_fingerprint().unwrap().to_string(),
+ "67A4 8753 A380 A6B3 B7DF 7DC5 E6C6 897A 4CEF 8924");
+ } else {
+ panic!("expected signature packet");
+ }
+}
+```
+
+# Streaming packet parsing
+
+Both the [`Message`]parser and the [`PacketPile`]parser build a tree
+structure in memory, and more importantly, they buffer the bodies of
+literal data packets. Both properties can be undesirable if a large
+number of packets is parsed, or the data contained in the message
+large. This problem is exacerbated by the fact that OpenPGP messages
+can be compressed, so that processing even small messages can lead to
+an unbounded amount of memory being allocated.
+
+To alleviate this problem, Sequoia features streaming interfaces that
+implement [`io::Read`] and [`io::Write`]. These interfaces allow
+processing of OpenPGP packets in constant space.
+
+[`io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
+[`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
+
+The core of Sequoia is our [`PacketParser`], upon which all higher
+level interfaces are built. It is the most flexible interface for
+processing OpenPGP packets, and it is the foundation for our streaming
+interfaces. Most of the time, it is not necessary to use this
+interface, but nevertheless, our parser is exposed as part of our API
+and can be used to quickly process large amounts of OpenPGP packets,
+e.g. for collecting statistics about the SKS keyserver dump. For a
+complete example, see [`openpgp/examples/statistics.rs`].
+
+[`PacketParser`]: ../../sequoia_openpgp/parse/struct.PacketParser.html
+[`openpgp/examples/statistics.rs`]: https://gitlab.com/sequoia-pgp/sequoia/blob/master/openpgp/examples/statistics.rs
+
+```rust
+use std::io::Read;
+
+extern crate sequoia_openpgp as openpgp;
+use openpgp::parse::*;
+
+const MESSAGE: &str =
+ "-----BEGIN PGP MESSAGE-----
+
+ yMACA0JaaDYxQVkmU1nHKJOZA6l4wQTAABAAAAgACCAAUGaaCalNNxCUkepFQEtY
+ hKSO3zFBWSZTWTYaxwsA6l5AAMAAAAggADCATUZBKSNSCUkcxQVkmU1k2GscLAOp
+ eQADAAAAIIAAwgE1GQSkjUglJHMUFZJlNZNhrHCwDqXkAAwAAACCAAMIBNRkEpI1
+ IJSRzFBWSZTWUmfJVgAWotAANkAAAggAFBmgClRjNkhJTMqEqoN9JCSnC7kinChI
+ H89bU4A=
+ =eySo
+ -----END PGP MESSAGE-----";
+
+fn main() {
+ let mut bytes_read = 0;
+ let mut buf = vec![0; 1024 * 1024];
+
+ let mut ppr = PacketParser::from_bytes(MESSAGE.as_bytes()).unwrap();
+ while let PacketParserResult::Some(mut pp) = ppr {
+ // Match on the kind of packet here while it is in the parser.
+ if let openpgp::Packet::Literal(_) = pp.packet {
+ // Stream the content of the literal packet.
+ while let Ok(_) = pp.read_exact(&mut buf) {
+ bytes_read += buf.len();
+ }
+ }
+
+ // Start parsing the next packet.
+ ppr = pp.recurse().unwrap().1;
+ }
+
+ assert_eq!(bytes_read, 128 * 1024 * 1024); // 128 megabytes
+}
+```