diff options
Diffstat (limited to 'guide/src/chapter_02.md')
-rw-r--r-- | guide/src/chapter_02.md | 716 |
1 files changed, 534 insertions, 182 deletions
diff --git a/guide/src/chapter_02.md b/guide/src/chapter_02.md index 80d5cc1c..5db1cbdc 100644 --- a/guide/src/chapter_02.md +++ b/guide/src/chapter_02.md @@ -1,221 +1,573 @@ -Describes how to use some of Sequoia's parsers. +Describes key creation, encryption, and decryption. -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`]. +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`]. -[`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`]: +[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::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-----"; +use openpgp::serialize::stream::*; +use openpgp::parse::stream::*; +use openpgp::packet::key::SecretKey; + +const MESSAGE: &'static str = "дружба"; 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"); + // 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(SecretKey::Unencrypted { +# ref mpis, +# }) = key.secret() { +# mpis.clone() +# } else { +# unreachable!() +# }; +# +# Ok(Some(Secret::Asymmetric { +# identity: self.secret.fingerprint(), +# key: key, +# secret: secret, +# })) +# } +# } ``` -# Parsing OpenPGP messages +# Key generation -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: +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: -[this grammar]: https://tools.ietf.org/html/rfc4880#section-11.3 -[`Message`]: ../../sequoia_openpgp/struct.Message.html +[`TPKBuilder`]: ../../sequoia_openpgp/tpk/struct.TPKBuilder.html ```rust -extern crate sequoia_openpgp as openpgp; -use openpgp::parse::Parse; +# use std::io::{self, Write}; +# +# extern crate sequoia_openpgp as openpgp; +# use openpgp::serialize::stream::*; +# use openpgp::parse::stream::*; +# use openpgp::packet::key::SecretKey; +# +# 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(SecretKey::Unencrypted { +# ref mpis, +# }) = key.secret() { +# mpis.clone() +# } else { +# unreachable!() +# }; +# +# Ok(Some(Secret::Asymmetric { +# identity: self.secret.fingerprint(), +# key: key, +# secret: secret, +# })) +# } +# } +``` -const MESSAGE: &str = - "-----BEGIN PGP MESSAGE----- +# Encryption - xA0DAAoW5saJekzviSQByxBiAAAAAADYtdiv2KfZgtipwnUEABYKACcFglwJHYoW - IQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAIJ6APwK6FxtHXn8txDl - tBFsIXlOSLOs4BvArlZzZSMomIyFLAEAwCLJUChMICDxWXRlHxORqU5x6hlO3DdW - sl/1DAbnRgI= - =AqoO - -----END PGP MESSAGE-----"; +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. -fn main() { - let message = openpgp::Message::from_bytes(MESSAGE.as_bytes()).unwrap(); +[`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html - assert_eq!(message.body().and_then(|literal| literal.body()), - Some("صداقة".as_bytes())); +```rust +# use std::io::{self, Write}; +# +# extern crate sequoia_openpgp as openpgp; +# use openpgp::serialize::stream::*; +# use openpgp::parse::stream::*; +# use openpgp::packet::key::SecretKey; +# +# 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(SecretKey::Unencrypted { +# ref mpis, +# }) = key.secret() { +# mpis.clone() +# } else { +# unreachable!() +# }; +# +# Ok(Some(Secret::Asymmetric { +# identity: self.secret.fingerprint(), +# key: key, +# secret: secret, +# })) +# } +# } ``` -# Parsing packets into packet piles +# Decryption -[`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: +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. -[`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; +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. -const MESSAGE: &str = - "-----BEGIN PGP MESSAGE----- +[`VerificationHelper`]: ../../sequoia_openpgp/parse/stream/trait.VerificationHelper.html +[`DecryptionHelper`]: ../../sequoia_openpgp/parse/stream/trait.DecryptionHelper.html - xA0DAAoW5saJekzviSQByxBiAAAAAADYtdiv2KfZgtipwnUEABYKACcFglwJHYoW - IQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAIJ6APwK6FxtHXn8txDl - tBFsIXlOSLOs4BvArlZzZSMomIyFLAEAwCLJUChMICDxWXRlHxORqU5x6hlO3DdW - sl/1DAbnRgI= - =AqoO - -----END PGP MESSAGE-----"; +To decrypt messages, we create a [`Decryptor`] with our helper. +Decrypted data can be read from this using [`io::Read`]. -fn main() { - let pile = openpgp::PacketPile::from_bytes(MESSAGE.as_bytes()).unwrap(); +[`Decryptor`]: ../../sequoia_openpgp/parse/stream/struct.Decryptor.html +[`io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html - // For simplicity, turn the pile into a vector of packets. - let packets: Vec<openpgp::Packet> = pile.into_children().collect(); +```rust +# use std::io::{self, Write}; +# +# extern crate sequoia_openpgp as openpgp; +# use openpgp::serialize::stream::*; +# use openpgp::parse::stream::*; +# use openpgp::packet::key::SecretKey; +# +# 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(()) +} - // There are three packets in that message. - assert_eq!(packets.len(), 3); +struct Helper<'a> { + secret: &'a openpgp::TPK, +} - // 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"); +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()) } - // 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"); + fn check(&mut self, _sigs: Vec<Vec<VerificationResult>>) + -> openpgp::Result<()> { + // Implement your signature verification policy here. + Ok(()) } +} - // 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"); +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(SecretKey::Unencrypted { + ref mpis, + }) = key.secret() { + mpis.clone() + } else { + unreachable!() + }; + + Ok(Some(Secret::Asymmetric { + identity: self.secret.fingerprint(), + key: key, + secret: secret, + })) } } ``` -# 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`]. +# Further reading -[`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; +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`]. -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 -} -``` +[`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 |