diff options
Diffstat (limited to 'guide/src/chapter_01.md')
-rw-r--r-- | guide/src/chapter_01.md | 569 |
1 files changed, 569 insertions, 0 deletions
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 |