summaryrefslogtreecommitdiffstats
path: root/guide
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
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')
-rw-r--r--guide/Cargo.toml13
-rw-r--r--guide/build.rs70
-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
6 files changed, 987 insertions, 0 deletions
diff --git a/guide/Cargo.toml b/guide/Cargo.toml
new file mode 100644
index 00000000..ee0477db
--- /dev/null
+++ b/guide/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "sequoia-guide"
+version = "0.2.0"
+authors = [
+ "Justus Winter <justus@sequoia-pgp.org>",
+ "Kai Michaelis <kai@sequoia-pgp.org>",
+ "Neal H. Walfield <neal@sequoia-pgp.org>",
+]
+build = "build.rs"
+
+[dependencies]
+sequoia-openpgp = { path = "../openpgp", version = "0.2.0" }
+failure = "0.1.2"
diff --git a/guide/build.rs b/guide/build.rs
new file mode 100644
index 00000000..3d73a984
--- /dev/null
+++ b/guide/build.rs
@@ -0,0 +1,70 @@
+use std::env;
+use std::io::{self, Write, BufRead};
+use std::fs::{self, DirEntry};
+use std::ffi::OsString;
+use std::path::{Path, PathBuf};
+
+// one possible implementation of walking a directory only visiting files
+fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry) -> io::Result<()>)
+ -> io::Result<()> {
+ if dir.is_dir() {
+ for entry in fs::read_dir(dir)? {
+ let entry = entry?;
+ let path = entry.path();
+ if path.is_dir() {
+ visit_dirs(&path, cb)?;
+ } else {
+ cb(&entry)?;
+ }
+ }
+ }
+ Ok(())
+}
+
+fn manifest_dir() -> PathBuf {
+ PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap())
+}
+
+fn out_dir() -> PathBuf {
+ env::var_os("OUT_DIR").unwrap().into()
+}
+
+fn lib_path() -> PathBuf {
+ out_dir().join("src/lib.rs")
+}
+
+fn translate_path(src: &Path) -> PathBuf {
+ let src = src.to_str().unwrap();
+ format!("{}rs", &src[..src.len() - 2]).into()
+}
+
+fn translate2rs(src: &DirEntry) -> io::Result<()> {
+ let path = src.path();
+ if path.extension() != Some(&OsString::from("md")) {
+ return Ok(());
+ }
+
+ let sink_filename = out_dir().join(
+ translate_path(&path).strip_prefix(&manifest_dir()).unwrap());
+
+ eprintln!("{:?} -> {:?}", path, sink_filename);
+ println!("rerun-if-changed={}", path.to_str().unwrap());
+
+ fs::create_dir_all(sink_filename.parent().unwrap())?;
+ let mut sink = fs::File::create(sink_filename)?;
+
+ for line in io::BufReader::new(fs::File::open(&path)?).lines() {
+ writeln!(&mut sink, "//! {}", line?)?;
+ }
+
+ let mut lib =
+ fs::OpenOptions::new().create(true).append(true).open(&lib_path())?;
+ writeln!(&mut lib, "pub mod {};", path.file_stem().unwrap().to_str().unwrap())?;
+
+ Ok(())
+}
+
+fn main() {
+ let _ = fs::remove_file(&lib_path());
+ visit_dirs(&manifest_dir().join("src"), &translate2rs).unwrap();
+}
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-----";
+
<