summaryrefslogtreecommitdiffstats
path: root/guide/src
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2018-10-18 16:40:56 +0200
committerJustus Winter <justus@sequoia-pgp.org>2018-12-07 11:44:25 +0100
commit22fe5f5fa08b7d98a05a9394320d7984083cd62f (patch)
tree766c91458bee19a064f7b0b0740a99eae97a5b20 /guide/src
parent614b24bf61e91525d7e22203405f097e065cbb3e (diff)
guide: New crate.
- Our previous guide published on our web site quickly bitrotted away. This guide, however, is tested as part of Sequoia's test suite, making sure that it stays in sync.
Diffstat (limited to 'guide/src')
-rw-r--r--guide/src/chapter_00.md95
-rw-r--r--guide/src/chapter_01.md569
-rw-r--r--guide/src/chapter_02.md218
-rw-r--r--guide/src/lib.rs22
4 files changed, 904 insertions, 0 deletions
diff --git a/guide/src/chapter_00.md b/guide/src/chapter_00.md
new file mode 100644
index 00000000..63eed991
--- /dev/null
+++ b/guide/src/chapter_00.md
@@ -0,0 +1,95 @@
+Describes how to create a simple Rust application using Sequoia.
+
+# Build dependencies
+
+First of all, you need Rust, and a few libraries that we depend upon.
+On Debian-like systems, the required packages can be installed using
+the following command. As of this writing, this works fine on Debian
+10 (Buster). You can use Debian 9 (Stretch), but you need to pull
+`rustc`, `cargo`, and `nettle-dev` from testing.
+
+```text
+# apt install git rustc cargo clang make pkg-config nettle-dev libssl-dev capnproto libsqlite3-dev
+```
+
+# Creating a new project
+
+If are starting from scratch, you need to create a new crate:
+
+```text
+$ cargo new --bin example
+ Created binary (application) `example` project
+$ cd example
+```
+
+Now add Sequoia to the `[dependencies]` section in `Cargo.toml`:
+
+```toml
+sequoia-openpgp = "0.2"
+```
+
+If you want to use the bleeding edge, you can instead refer to the
+version in git:
+
+```toml
+sequoia-openpgp = { git = "https://gitlab.com/sequoia-pgp/sequoia.git" }
+```
+
+To build and run your application, do:
+
+```sh
+$ cargo run
+```
+
+On the first run, cargo will download and build Sequoia and all
+dependencies. When finished, nothing really happens because we have
+not populated `main` yet. Let's do that! Open `src/main.rs` with
+your favorite editor, and enter:
+
+```
+#[macro_use] // For armored!
+extern crate sequoia_openpgp as openpgp;
+use std::io;
+
+fn main() {
+ let mut reader = armored!(
+ "-----BEGIN PGP ARMORED FILE-----
+
+ SGVsbG8gd29ybGQhCg==
+ =XLsG
+ -----END PGP ARMORED FILE-----"
+ );
+
+ io::copy(&mut reader, &mut io::stdout()).unwrap();
+}
+```
+
+Running the application now prints a friendly message to stdout.
+
+A word on the `armored` macro. We will use this macro in this guide
+to inline OpenPGP data into the source code. Sequoia includes filters
+for ASCII armored data. You can use these filters to read armored
+data from any `Read`er, or write armored data to any `Write`r. The
+`armored` macro does the same for string literals. In order to use
+this macro, you need to use `#[macro_use]` when importing the
+`openpgp` crate.
+
+# Building the Sequoia tool
+
+Sequoia includes a simple frontend `sq` that can be used to experiment
+with Sequoia and OpenPGP. The documentation for this tool is
+[here](../../sq/index.html). It is also an example of
+how to use various aspects of Sequoia. Clone Sequoia and build the
+tool:
+
+```sh
+$ git clone https://gitlab.com/sequoia-pgp/sequoia.git
+...
+$ cd sequoia
+$ cargo build -p sequoia-tool
+...
+$ target/debug/sq
+sq 0.1.0
+Sequoia is an implementation of OpenPGP. This is a command-line frontend.
+...
+```
diff --git a/guide/src/chapter_01.md b/guide/src/chapter_01.md
new file mode 100644
index 00000000..e81354e5
--- /dev/null
+++ b/guide/src/chapter_01.md
@@ -0,0 +1,569 @@
+Describes key creation, encryption, and decryption.
+
+In this chapter, we will see how to use Sequoia's [low-level API] to
+generate an OpenPGP key, and use it to encrypt and decrypt some data.
+We will construct this program from top to bottom, concatenating the
+fragments yields the [`openpgp/examples/generate-encrypt-decrypt.rs`].
+
+[low-level API]: ../../sequoia_openpgp/index.html
+[`openpgp/examples/generate-encrypt-decrypt.rs`]: https://gitlab.com/sequoia-pgp/sequoia/blob/master/openpgp/examples/generate-encrypt-decrypt.rs
+
+```rust
+use std::io::{self, Write};
+
+extern crate sequoia_openpgp as openpgp;
+use openpgp::serialize::stream::*;
+use openpgp::parse::stream::*;
+
+const MESSAGE: &'static str = "дружба";
+
+fn main() {
+ // Generate a key.
+ let key = generate().unwrap();
+
+ // Encrypt the message.
+ let mut ciphertext = Vec::new();
+ encrypt(&mut ciphertext, MESSAGE, &key).unwrap();
+
+ // Decrypt the message.
+ let mut plaintext = Vec::new();
+ decrypt(&mut plaintext, &ciphertext, &key).unwrap();
+
+ assert_eq!(MESSAGE.as_bytes(), &plaintext[..]);
+}
+#
+# /// Generates an encryption-capable key.
+# fn generate() -> openpgp::Result<openpgp::TPK> {
+# let (tpk, _revocation) = openpgp::tpk::TPKBuilder::default()
+# .add_userid("someone@example.org")
+# .add_encryption_subkey()
+# .generate()?;
+#
+# // Save the revocation certificate somewhere.
+#
+# Ok(tpk)
+# }
+#
+# /// Encrypts the given message.
+# fn encrypt(sink: &mut Write, plaintext: &str, recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Start streaming an OpenPGP message.
+# let message = Message::new(sink);
+#
+# // We want to encrypt a literal data packet.
+# let encryptor = Encryptor::new(message,
+# &[], // No symmetric encryption.
+# &[recipient],
+# EncryptionMode::ForTransport)?;
+#
+# // Emit a literal data packet.
+# let mut literal_writer = LiteralWriter::new(
+# encryptor, openpgp::constants::DataFormat::Binary, None, None)?;
+#
+# // Encrypt the data.
+# literal_writer.write_all(plaintext.as_bytes())?;
+#
+# // Finalize the OpenPGP message to make sure that all data is
+# // written.
+# literal_writer.finalize()?;
+#
+# Ok(())
+# }
+#
+# /// Decrypts the given message.
+# fn decrypt(sink: &mut Write, ciphertext: &[u8], recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Make a helper that that feeds the recipient's secret key to the
+# // decryptor.
+# let helper = Helper {
+# secret: recipient,
+# };
+#
+# // Now, create a decryptor with a helper using the given TPKs.
+# let mut decryptor = Decryptor::from_bytes(ciphertext, helper)?;
+#
+# // Decrypt the data.
+# io::copy(&mut decryptor, sink)?;
+#
+# Ok(())
+# }
+#
+# struct Helper<'a> {
+# secret: &'a openpgp::TPK,
+# }
+#
+# impl<'a> VerificationHelper for Helper<'a> {
+# fn get_public_keys(&mut self, _ids: &[openpgp::KeyID])
+# -> openpgp::Result<Vec<openpgp::TPK>> {
+# // Return public keys for signature verification here.
+# Ok(Vec::new())
+# }
+#
+# fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>)
+# -> openpgp::Result<()> {
+# // Implement your signature verification policy here.
+# Ok(())
+# }
+# }
+#
+# impl<'a> DecryptionHelper for Helper<'a> {
+# fn get_secret(&mut self,
+# _pkesks: &[&openpgp::packet::PKESK],
+# _skesks: &[&openpgp::packet::SKESK])
+# -> openpgp::Result<Option<Secret>>
+# {
+# // The encryption key is the first and only subkey.
+# let key = self.secret.subkeys().nth(0)
+# .map(|binding| binding.subkey().clone())
+# .unwrap();
+#
+# // The secret key is not encrypted.
+# let secret =
+# if let Some(openpgp::SecretKey::Unencrypted {
+# ref mpis,
+# }) = key.secret() {
+# mpis.clone()
+# } else {
+# unreachable!()
+# };
+#
+# Ok(Some(Secret::Asymmetric {
+# identity: self.secret.fingerprint(),
+# key: key,
+# secret: secret,
+# }))
+# }
+# }
+```
+
+# Key generation
+
+First, we need to generate a new key. This key shall have one user
+id, and one encryption-capable subkey. We use the [`TPKBuilder`] to
+create it:
+
+[`TPKBuilder`]: ../../sequoia_openpgp/tpk/struct.TPKBuilder.html
+
+```rust
+# use std::io::{self, Write};
+#
+# extern crate sequoia_openpgp as openpgp;
+# use openpgp::serialize::stream::*;
+# use openpgp::parse::stream::*;
+#
+# const MESSAGE: &'static str = "дружба";
+#
+# fn main() {
+# // Generate a key.
+# let key = generate().unwrap();
+#
+# // Encrypt the message.
+# let mut ciphertext = Vec::new();
+# encrypt(&mut ciphertext, MESSAGE, &key).unwrap();
+#
+# // Decrypt the message.
+# let mut plaintext = Vec::new();
+# decrypt(&mut plaintext, &ciphertext, &key).unwrap();
+#
+# assert_eq!(MESSAGE.as_bytes(), &plaintext[..]);
+# }
+#
+/// Generates an encryption-capable key.
+fn generate() -> openpgp::Result<openpgp::TPK> {
+ let (tpk, _revocation) = openpgp::tpk::TPKBuilder::default()
+ .add_userid("someone@example.org")
+ .add_encryption_subkey()
+ .generate()?;
+
+ // Save the revocation certificate somewhere.
+
+ Ok(tpk)
+}
+#
+# /// Encrypts the given message.
+# fn encrypt(sink: &mut Write, plaintext: &str, recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Start streaming an OpenPGP message.
+# let message = Message::new(sink);
+#
+# // We want to encrypt a literal data packet.
+# let encryptor = Encryptor::new(message,
+# &[], // No symmetric encryption.
+# &[recipient],
+# EncryptionMode::ForTransport)?;
+#
+# // Emit a literal data packet.
+# let mut literal_writer = LiteralWriter::new(
+# encryptor, openpgp::constants::DataFormat::Binary, None, None)?;
+#
+# // Encrypt the data.
+# literal_writer.write_all(plaintext.as_bytes())?;
+#
+# // Finalize the OpenPGP message to make sure that all data is
+# // written.
+# literal_writer.finalize()?;
+#
+# Ok(())
+# }
+#
+# /// Decrypts the given message.
+# fn decrypt(sink: &mut Write, ciphertext: &[u8], recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Make a helper that that feeds the recipient's secret key to the
+# // decryptor.
+# let helper = Helper {
+# secret: recipient,
+# };
+#
+# // Now, create a decryptor with a helper using the given TPKs.
+# let mut decryptor = Decryptor::from_bytes(ciphertext, helper)?;
+#
+# // Decrypt the data.
+# io::copy(&mut decryptor, sink)?;
+#
+# Ok(())
+# }
+#
+# struct Helper<'a> {
+# secret: &'a openpgp::TPK,
+# }
+#
+# impl<'a> VerificationHelper for Helper<'a> {
+# fn get_public_keys(&mut self, _ids: &[openpgp::KeyID])
+# -> openpgp::Result<Vec<openpgp::TPK>> {
+# // Return public keys for signature verification here.
+# Ok(Vec::new())
+# }
+#
+# fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>)
+# -> openpgp::Result<()> {
+# // Implement your signature verification policy here.
+# Ok(())
+# }
+# }
+#
+# impl<'a> DecryptionHelper for Helper<'a> {
+# fn get_secret(&mut self,
+# _pkesks: &[&openpgp::packet::PKESK],
+# _skesks: &[&openpgp::packet::SKESK])
+# -> openpgp::Result<Option<Secret>>
+# {
+# // The encryption key is the first and only subkey.
+# let key = self.secret.subkeys().nth(0)
+# .map(|binding| binding.subkey().clone())
+# .unwrap();
+#
+# // The secret key is not encrypted.
+# let secret =
+# if let Some(openpgp::SecretKey::Unencrypted {
+# ref mpis,
+# }) = key.secret() {
+# mpis.clone()
+# } else {
+# unreachable!()
+# };
+#
+# Ok(Some(Secret::Asymmetric {
+# identity: self.secret.fingerprint(),
+# key: key,
+# secret: secret,
+# }))
+# }
+# }
+```
+
+# Encryption
+
+To encrypt a message, we first compose a writer stack corresponding to
+the desired output format and packet structure. The resulting object
+implements [`io::Write`], and we simply write the plaintext to it.
+
+[`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
+
+```rust
+# use std::io::{self, Write};
+#
+# extern crate sequoia_openpgp as openpgp;
+# use openpgp::serialize::stream::*;
+# use openpgp::parse::stream::*;
+#
+# const MESSAGE: &'static str = "дружба";
+#
+# fn main() {
+# // Generate a key.
+# let key = generate().unwrap();
+#
+# // Encrypt the message.
+# let mut ciphertext = Vec::new();
+# encrypt(&mut ciphertext, MESSAGE, &key).unwrap();
+#
+# // Decrypt the message.
+# let mut plaintext = Vec::new();
+# decrypt(&mut plaintext, &ciphertext, &key).unwrap();
+#
+# assert_eq!(MESSAGE.as_bytes(), &plaintext[..]);
+# }
+#
+# /// Generates an encryption-capable key.
+# fn generate() -> openpgp::Result<openpgp::TPK> {
+# let (tpk, _revocation) = openpgp::tpk::TPKBuilder::default()
+# .add_userid("someone@example.org")
+# .add_encryption_subkey()
+# .generate()?;
+#
+# // Save the revocation certificate somewhere.
+#
+# Ok(tpk)
+# }
+#
+/// Encrypts the given message.
+fn encrypt(sink: &mut Write, plaintext: &str, recipient: &openpgp::TPK)
+ -> openpgp::Result<()> {
+ // Start streaming an OpenPGP message.
+ let message = Message::new(sink);
+
+ // We want to encrypt a literal data packet.
+ let encryptor = Encryptor::new(message,
+ &[], // No symmetric encryption.
+ &[recipient],
+ EncryptionMode::ForTransport)?;
+
+ // Emit a literal data packet.
+ let mut literal_writer = LiteralWriter::new(
+ encryptor, openpgp::constants::DataFormat::Binary, None, None)?;
+
+ // Encrypt the data.
+ literal_writer.write_all(plaintext.as_bytes())?;
+
+ // Finalize the OpenPGP message to make sure that all data is
+ // written.
+ literal_writer.finalize()?;
+
+ Ok(())
+}
+#
+# /// Decrypts the given message.
+# fn decrypt(sink: &mut Write, ciphertext: &[u8], recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Make a helper that that feeds the recipient's secret key to the
+# // decryptor.
+# let helper = Helper {
+# secret: recipient,
+# };
+#
+# // Now, create a decryptor with a helper using the given TPKs.
+# let mut decryptor = Decryptor::from_bytes(ciphertext, helper)?;
+#
+# // Decrypt the data.
+# io::copy(&mut decryptor, sink)?;
+#
+# Ok(())
+# }
+#
+# struct Helper<'a> {
+# secret: &'a openpgp::TPK,
+# }
+#
+# impl<'a> VerificationHelper for Helper<'a> {
+# fn get_public_keys(&mut self, _ids: &[openpgp::KeyID])
+# -> openpgp::Result<Vec<openpgp::TPK>> {
+# // Return public keys for signature verification here.
+# Ok(Vec::new())
+# }
+#
+# fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>)
+# -> openpgp::Result<()> {
+# // Implement your signature verification policy here.
+# Ok(())
+# }
+# }
+#
+# impl<'a> DecryptionHelper for Helper<'a> {
+# fn get_secret(&mut self,
+# _pkesks: &[&openpgp::packet::PKESK],
+# _skesks: &[&openpgp::packet::SKESK])
+# -> openpgp::Result<Option<Secret>>
+# {
+# // The encryption key is the first and only subkey.
+# let key = self.secret.subkeys().nth(0)
+# .map(|binding| binding.subkey().clone())
+# .unwrap();
+#
+# // The secret key is not encrypted.
+# let secret =
+# if let Some(openpgp::SecretKey::Unencrypted {
+# ref mpis,
+# }) = key.secret() {
+# mpis.clone()
+# } else {
+# unreachable!()
+# };
+#
+# Ok(Some(Secret::Asymmetric {
+# identity: self.secret.fingerprint(),
+# key: key,
+# secret: secret,
+# }))
+# }
+# }
+```
+
+# Decryption
+
+Decryption is more difficult than encryption. When we encrypt, we
+control the packet structure being generated. However, when we
+decrypt, the control flow is determined by the message being
+processed.
+
+To use Sequoia's low-level streaming decryptor, we need to provide an
+object that implements [`VerificationHelper`] and
+[`DecryptionHelper`]. This object provides public and secret keys for
+the signature verification and decryption, and implements the
+signature verification policy.
+
+[`VerificationHelper`]: ../../sequoia_openpgp/parse/stream/trait.VerificationHelper.html
+[`DecryptionHelper`]: ../../sequoia_openpgp/parse/stream/trait.DecryptionHelper.html
+
+To decrypt messages, we create a [`Decryptor`] with our helper.
+Decrypted data can be read from this using [`io::Read`].
+
+[`Decryptor`]: ../../sequoia_openpgp/parse/stream/struct.Decryptor.html
+[`io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
+
+```rust
+# use std::io::{self, Write};
+#
+# extern crate sequoia_openpgp as openpgp;
+# use openpgp::serialize::stream::*;
+# use openpgp::parse::stream::*;
+#
+# const MESSAGE: &'static str = "дружба";
+#
+# fn main() {
+# // Generate a key.
+# let key = generate().unwrap();
+#
+# // Encrypt the message.
+# let mut ciphertext = Vec::new();
+# encrypt(&mut ciphertext, MESSAGE, &key).unwrap();
+#
+# // Decrypt the message.
+# let mut plaintext = Vec::new();
+# decrypt(&mut plaintext, &ciphertext, &key).unwrap();
+#
+# assert_eq!(MESSAGE.as_bytes(), &plaintext[..]);
+# }
+#
+# /// Generates an encryption-capable key.
+# fn generate() -> openpgp::Result<openpgp::TPK> {
+# let (tpk, _revocation) = openpgp::tpk::TPKBuilder::default()
+# .add_userid("someone@example.org")
+# .add_encryption_subkey()
+# .generate()?;
+#
+# // Save the revocation certificate somewhere.
+#
+# Ok(tpk)
+# }
+#
+# /// Encrypts the given message.
+# fn encrypt(sink: &mut Write, plaintext: &str, recipient: &openpgp::TPK)
+# -> openpgp::Result<()> {
+# // Start streaming an OpenPGP message.
+# let message = Message::new(sink);
+#
+# // We want to encrypt a literal data packet.
+# let encryptor = Encryptor::new(message,
+# &[], // No symmetric encryption.
+# &[recipient],
+# EncryptionMode::ForTransport)?;
+#
+# // Emit a literal data packet.
+# let mut literal_writer = LiteralWriter::new(
+# encryptor, openpgp::constants::DataFormat::Binary, None, None)?;
+#
+# // Encrypt the data.
+# literal_writer.write_all(plaintext.as_bytes())?;
+#
+# // Finalize the OpenPGP message to make sure that all data is
+# // written.
+# literal_writer.finalize()?;
+#
+# Ok(())
+# }
+#
+/// Decrypts the given message.
+fn decrypt(sink: &mut Write, ciphertext: &[u8], recipient: &openpgp::TPK)
+ -> openpgp::Result<()> {
+ // Make a helper that that feeds the recipient's secret key to the
+ // decryptor.
+ let helper = Helper {
+ secret: recipient,
+ };
+
+ // Now, create a decryptor with a helper using the given TPKs.
+ let mut decryptor = Decryptor::from_bytes(ciphertext, helper)?;
+
+ // Decrypt the data.
+ io::copy(&mut decryptor, sink)?;
+
+ Ok(())
+}
+
+struct Helper<'a> {
+ secret: &'a openpgp::TPK,
+}
+
+impl<'a> VerificationHelper for Helper<'a> {
+ fn get_public_keys(&mut self, _ids: &[openpgp::KeyID])
+ -> openpgp::Result<Vec<openpgp::TPK>> {
+ // Return public keys for signature verification here.
+ Ok(Vec::new())
+ }
+
+ fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>)
+ -> openpgp::Result<()> {
+ // Implement your signature verification policy here.
+ Ok(())
+ }
+}
+
+impl<'a> DecryptionHelper for Helper<'a> {
+ fn get_secret(&mut self,
+ _pkesks: &[&openpgp::packet::PKESK],
+ _skesks: &[&openpgp::packet::SKESK])
+ -> openpgp::Result<Option<Secret>>
+ {
+ // The encryption key is the first and only subkey.
+ let key = self.secret.subkeys().nth(0)
+ .map(|binding| binding.subkey().clone())
+ .unwrap();
+
+ // The secret key is not encrypted.
+ let secret =
+ if let Some(openpgp::SecretKey::Unencrypted {
+ ref mpis,
+ }) = key.secret() {
+ mpis.clone()
+ } else {
+ unreachable!()
+ };
+
+ Ok(Some(Secret::Asymmetric {
+ identity: self.secret.fingerprint(),
+ key: key,
+ secret: secret,
+ }))
+ }
+}
+```
+
+# Further reading
+
+For more examples on how to read a key from a file, and then either
+encrypt or decrypt some messages, see
+[`openpgp/examples/encrypt-for.rs`] and
+[`openpgp/examples/decrypt-with.rs`].
+
+[`openpgp/examples/encrypt-for.rs`]: https://gitlab.com/sequoia-pgp/sequoia/blob/master/openpgp/examples/encrypt-for.rs
+[`openpgp/examples/decrypt-with.rs`]: https://gitlab.com/sequoia-pgp/sequoia/blob/master/openpgp/examples/decrypt-with.rs
diff --git a/guide/src/chapter_02.md b/guide/src/chapter_02.md
new file mode 100644
index 00000000..f39cecd1
--- /dev/null
+++ b/guide/src/chapter_02.md
@@ -0,0 +1,218 @@
+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;
+
+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;
+
+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;
+
+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");
+ }