diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-10-18 16:40:56 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-12-07 11:44:25 +0100 |
commit | 22fe5f5fa08b7d98a05a9394320d7984083cd62f (patch) | |
tree | 766c91458bee19a064f7b0b0740a99eae97a5b20 | |
parent | 614b24bf61e91525d7e22203405f097e065cbb3e (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.
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | guide/Cargo.toml | 13 | ||||
-rw-r--r-- | guide/build.rs | 70 | ||||
-rw-r--r-- | guide/src/chapter_00.md | 95 | ||||
-rw-r--r-- | guide/src/chapter_01.md | 569 | ||||
-rw-r--r-- | guide/src/chapter_02.md | 218 | ||||
-rw-r--r-- | guide/src/lib.rs | 22 |
7 files changed, 988 insertions, 0 deletions
@@ -30,6 +30,7 @@ sequoia-net = { path = "net", version = "0.2.0" } sequoia-store = { path = "store", version = "0.2.0" } sequoia-tool = { path = "tool", version = "0.2.0" } sequoia-sqv = { path = "sqv", version = "0.2.0" } +sequoia-guide = { path = "guide", version = "0.2.0" } [lib] name = "sequoia" 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 = |