diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-04-07 13:19:12 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-04-07 14:35:48 +0200 |
commit | f6a00b3eee40fa92339d55c55b6806e9324152fa (patch) | |
tree | b4f45cf41850f0f1c227bb09d739f0821b3ff3d4 /openpgp/src/serialize/stream.rs | |
parent | 38eab42143318a622759dc98a5cc5ecd5df9b9b4 (diff) |
openpgp: Improve documentation for the serialize module.
- Fixes #472.
Diffstat (limited to 'openpgp/src/serialize/stream.rs')
-rw-r--r-- | openpgp/src/serialize/stream.rs | 1477 |
1 files changed, 1264 insertions, 213 deletions
diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 6ccbc116..c2d8ef12 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -1,13 +1,123 @@ //! Streaming packet serialization. //! -//! This is the preferred interface to generate OpenPGP messages. It +//! This interface provides a convenient way to create signed and/or +//! encrypted OpenPGP messages (see [Section 11.3 of RFC 4880]) and is +//! the preferred interface to generate messages using Sequoia. It //! takes advantage of OpenPGP's streaming nature to avoid unnecessary -//! buffering. This interface provides a convenient, yet low-level -//! way to sign or encrypt. +//! buffering. //! -//! See the [encryption example]. +//! [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3 //! -//! [encryption example]: struct.Encryptor.html#example +//! To use this interface, a sink implementing [`io::Write`] is wrapped +//! by [`Message::new`] returning an [`writer::Stack`]. The writer +//! stack is a structure to compose filters that create the desired +//! message structure. There are a number of filters that can be +//! freely combined: +//! +//! - [`Encryptor`] encrypts data fed into it, +//! - [`Compressor`] compresses data, +//! - [`Padder`] pads data, +//! - [`Signer`] signs data, +//! - [`LiteralWriter`] wraps literal data (i.e. the payload) into +//! a literal data packet, +//! - and finally, [`ArbitraryWriter`] can be used to create +//! arbitrary packets for testing purposes. +//! +//! [`io::Write`]: https://doc.rust-lang.org/nightly/std/io/trait.Write.html +//! [`Message::new`]: struct.Message.html#method.new +//! [`writer::Stack`]: ../writer/struct.Stack.html +//! [`Encryptor`]: struct.Encryptor.html +//! [`Compressor`]: struct.Compressor.html +//! [`Padder`]: padding/struct.Padder.html +//! [`Signer`]: struct.Signer.html +//! [`LiteralWriter`]: struct.LiteralWriter.html +//! [`ArbitraryWriter`]: struct.ArbitraryWriter.html +//! +//! The most common structure is an encrypted, compressed, and signed +//! message. This structure is [supported] by all OpenPGP +//! implementations. See the example below on how to create this +//! structure. +//! +//! [supported]: https://tests.sequoia-pgp.org/#Unusual_Message_Structure +//! +//! # Examples +//! +//! This example demonstrates how to create the simplest possible +//! OpenPGP message (see [Section 11.3 of RFC 4880]) containing just a +//! literal data packet (see [Section 5.9 of RFC 4880]): +//! +//! [Section 5.9 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.9 +//! +//! ``` +//! # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { +//! use std::io::Write; +//! use sequoia_openpgp as openpgp; +//! use openpgp::serialize::stream::{Message, LiteralWriter}; +//! +//! let mut sink = vec![]; +//! { +//! let message = Message::new(&mut sink); +//! let mut message = LiteralWriter::new(message).build()?; +//! message.write_all(b"Hello world.")?; +//! message.finalize()?; +//! } +//! assert_eq!(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.", sink.as_slice()); +//! # Ok(()) } +//! ``` +//! +//! This example demonstrates how to create the most common OpenPGP +//! message structure (see [Section 11.3 of RFC 4880]). The plaintext +//! is first signed, then compressed, and finally encrypted. Our +//! example pads the plaintext instead of compressing it, but the +//! resulting message structure is the same. +//! +//! ``` +//! # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { +//! use std::io::Write; +//! use sequoia_openpgp as openpgp; +//! use openpgp::policy::StandardPolicy; +//! use openpgp::cert::prelude::*; +//! use openpgp::serialize::stream::{ +//! Message, Encryptor, Signer, LiteralWriter, +//! padding::{Padder, padme}, +//! }; +//! # use openpgp::parse::Parse; +//! +//! let p = &StandardPolicy::new(); +//! +//! let sender: Cert = // ... +//! # Cert::from_bytes(&include_bytes!( +//! # "../../tests/data/keys/testy-new-private.pgp")[..])?; +//! let signing_keypair = sender.keys().secret() +//! .with_policy(p, None).alive().revoked(false).for_signing() +//! .nth(0).unwrap() +//! .key().clone().into_keypair()?; +//! +//! let recipient: Cert = // ... +//! # sender.clone(); +//! // Build a vector of recipients to hand to the `Encryptor`. +//! // Note: One certificate may contain several suitable encryption keys. +//! let recipients = +//! recipient.keys().with_policy(p, None).alive().revoked(false) +//! // Or `for_storage_encryption()`, for data at rest. +//! .for_transport_encryption() +//! .map(|ka| ka.key().into()) +//! .collect(); +//! +//! # let mut sink = vec![]; +//! let message = Message::new(&mut sink); +//! let message = Encryptor::for_recipients(message, recipients).build()?; +//! // Reduce metadata leakage by concealing the message size. +//! let message = Padder::new(message, padme)?; +//! let message = Signer::new(message, signing_keypair) +//! // Prevent Surreptitious Forwarding. +//! .add_intended_recipient(&recipient) +//! .build()?; +//! let mut message = LiteralWriter::new(message).build()?; +//! message.write_all(b"Hello world.")?; +//! message.finalize()?; +//! # Ok(()) } +//! ``` use std::fmt; use std::io::{self, Write}; @@ -83,22 +193,125 @@ impl Default for Cookie { /// Streams an OpenPGP message. /// -/// Wraps a `std::io::Write`r for use with the streaming subsystem. +/// Wraps an [`io::Write`]r for use with the streaming subsystem. The +/// `Message` is a stack of filters that create the desired message +/// structure. Literal data must be framed using the +/// [`LiteralWriter`] filter. Once all the has been written, the +/// `Message` must be finalized using [`Message::finalize`]. +/// +/// [`io::Write`]: https://doc.rust-lang.org/nightly/std/io/trait.Write.html +/// [`LiteralWriter`]: struct.LiteralWriter.html +/// [`Message::finalize`]: #method.finalize #[derive(Debug)] pub struct Message<'a, C>(writer::BoxStack<'a, C>); impl<'a> Message<'a, Cookie> { - /// Streams an OpenPGP message. + /// Starts streaming an OpenPGP message. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use sequoia_openpgp as openpgp; + /// use openpgp::serialize::stream::{Message, LiteralWriter}; + /// + /// # let mut sink = vec![]; // Vec<u8> implements io::Write. + /// let message = Message::new(&mut sink); + /// // Construct the writer stack here. + /// let mut message = LiteralWriter::new(message).build()?; + /// // Write literal data to `message` here. + /// // ... + /// // Finalize the message. + /// message.finalize()?; + /// # Ok(()) } + /// ``` pub fn new<W: 'a + io::Write>(w: W) -> Message<'a, Cookie> { writer::Generic::new(w, Cookie::new(0)) } - /// Finalizes this writer, returning the underlying writer. + /// Finalizes the topmost writer, returning the underlying writer. + /// + /// Finalizes the topmost writer, i.e. flushes any buffered data, + /// and pops it of the stack. This allows for fine-grained + /// control of the resulting message, but must be done with great + /// care. If done improperly, the resulting message may be + /// malformed. + /// + /// # Example + /// + /// This demonstrates how to create a compressed, signed message + /// from a detached signature. + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use std::convert::TryFrom; + /// use sequoia_openpgp as openpgp; + /// use openpgp::packet::{Packet, Signature, one_pass_sig::OnePassSig3}; + /// # use openpgp::parse::Parse; + /// use openpgp::serialize::Serialize; + /// use openpgp::serialize::stream::{Message, Compressor, LiteralWriter}; + /// + /// let data: &[u8] = // ... + /// # &include_bytes!( + /// # "../../tests/data/messages/a-cypherpunks-manifesto.txt")[..]; + /// let sig: Signature = // ... + /// # if let Packet::Signature(s) = Packet::from_bytes(&include_bytes!( + /// # "../../tests/data/messages/a-cypherpunks-manifesto.txt.ed25519.sig")[..])? + /// # { s } else { panic!() }; + /// + /// # let mut sink = vec![]; // Vec<u8> implements io::Write. + /// let message = Message::new(&mut sink); + /// let mut message = Compressor::new(message).build()?; + /// + /// // First, write a one-pass-signature packet. + /// Packet::from(OnePassSig3::try_from(&sig)?) + /// .serialize(&mut message)?; + /// + /// // Then, add the literal data. + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(data)?; + /// + /// // Finally, pop the `LiteralWriter` off the stack to write the + /// // signature. + /// let mut message = message.finalize_one()?.unwrap(); + /// Packet::from(sig).serialize(&mut message)?; + /// + /// // Finalize the message. + /// message.finalize()?; + /// # Ok(()) } + /// ``` pub fn finalize_one(self) -> Result<Option<Message<'a, Cookie>>> { Ok(self.0.into_inner()?.map(|bs| Self::from(bs))) } - /// Finalizes all writers, tearing down the whole stack. + /// Finalizes the message. + /// + /// Finalizes all writers on the stack, flushing any buffered + /// data. + /// + /// # Note + /// + /// Failing to finalize the message may result in corrupted + /// messages. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use sequoia_openpgp as openpgp; + /// use openpgp::serialize::stream::{Message, LiteralWriter}; + /// + /// # let mut sink = vec![]; // Vec<u8> implements io::Write. + /// let message = Message::new(&mut sink); + /// // Construct the writer stack here. + /// let mut message = LiteralWriter::new(message).build()?; + /// // Write literal data to `message` here. + /// // ... + /// // Finalize the message. + /// message.finalize()?; + /// # Ok(()) } + /// ``` pub fn finalize(self) -> Result<()> { let mut stack = self; while let Some(s) = stack.finalize_one()? { @@ -118,36 +331,37 @@ impl<'a> From<&'a mut dyn io::Write> for Message<'a, Cookie> { /// Writes an arbitrary packet. /// /// This writer can be used to construct arbitrary OpenPGP packets. -/// The body will be written using partial length encoding, or, if the -/// body is short, using full length encoding. -/// -/// # Example -/// -/// ``` -/// extern crate sequoia_openpgp as openpgp; -/// use std::io::Write; -/// use openpgp::packet::Tag; -/// use openpgp::serialize::stream::{Message, ArbitraryWriter}; -/// # use openpgp::Result; -/// # f().unwrap(); -/// # fn f() -> Result<()> { -/// let mut o = vec![]; -/// { -/// let mut w = ArbitraryWriter::new(Message::new(&mut o), Tag::Literal)?; -/// w.write_all(b"t")?; // type -/// w.write_all(b"\x00")?; // filename length -/// w.write_all(b"\x00\x00\x00\x00")?; // date -/// w.write_all(b"Hello world.")?; // body -/// } -/// assert_eq!(b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.", o.as_slice()); -/// # Ok(()) -/// # } +/// This is mainly useful for testing. The body will be written using +/// partial length encoding, or, if the body is short, using full +/// length encoding. pub struct ArbitraryWriter<'a> { inner: writer::BoxStack<'a, Cookie>, } impl<'a> ArbitraryWriter<'a> { /// Creates a new writer with the given tag. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::packet::Tag; + /// use openpgp::serialize::stream::{Message, ArbitraryWriter}; + /// + /// let mut sink = vec![]; + /// { + /// let message = Message::new(&mut sink); + /// let mut message = ArbitraryWriter::new(message, Tag::Literal)?; + /// message.write_all(b"t")?; // type + /// message.write_all(b"\x00")?; // filename length + /// message.write_all(b"\x00\x00\x00\x00")?; // date + /// message.write_all(b"Hello world.")?; // body + /// } + /// assert_eq!(b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.", + /// sink.as_slice()); + /// # Ok(()) } pub fn new(mut inner: Message<'a, Cookie>, tag: Tag) -> Result<Message<'a, Cookie>> { let level = inner.as_ref().cookie_ref().level + 1; @@ -206,11 +420,12 @@ impl<'a> writer::Stackable<'a, Cookie> for ArbitraryWriter<'a> { } } -/// Signs a packet stream. +/// Signs a message. +/// +/// Signs a message with every [`crypto::Signer`] added to the +/// streaming signer. /// -/// For every signing key, a signer writes a one-pass-signature -/// packet, then hashes and emits the data stream, then for every key -/// writes a signature packet. +/// [`crypto::Signer`]: ../../crypto/trait.Signer.html pub struct Signer<'a> { // The underlying writer. // @@ -234,39 +449,49 @@ pub struct Signer<'a> { impl<'a> Signer<'a> { /// Creates a signer. /// + /// Signs the message with the given [`crypto::Signer`]. To + /// create more than one signature, add more [`crypto::Signer`]s + /// using [`Signer::add_signer`]. Properties of the signatures + /// can be tweaked using the methods of this type. Notably, to + /// generate a detached signature (see [Section 11.4 of RFC + /// 4880]), use [`Signer::detached`]. + /// + /// [`crypto::Signer`]: ../../crypto/trait.Signer.html + /// [`Signer::add_signer`]: #method.add_signer + /// [Section 11.4 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.4 + /// [`Signer::detached`]: #method.detached + /// /// # Example /// /// ``` - /// extern crate sequoia_openpgp as openpgp; + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { /// use std::io::{Read, Write}; + /// use sequoia_openpgp as openpgp; /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; /// use openpgp::policy::StandardPolicy; /// # use openpgp::{Result, Cert}; /// # use openpgp::packet::prelude::*; - /// # use openpgp::crypto::KeyPair; /// # use openpgp::parse::Parse; /// # use openpgp::parse::stream::*; /// /// let p = &StandardPolicy::new(); + /// let cert: Cert = // ... + /// # Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// let signing_keypair = cert.keys().secret() + /// .with_policy(p, None).alive().revoked(false).for_signing() + /// .nth(0).unwrap() + /// .key().clone().into_keypair()?; /// - /// # let tsk = Cert::from_bytes(&include_bytes!( - /// # "../../tests/data/keys/testy-new-private.pgp")[..]) - /// # .unwrap(); - /// # let keypair = tsk.keys().with_policy(p, None).alive().revoked(false).for_signing() - /// # .nth(0).unwrap() - /// # .key().clone().parts_into_secret().unwrap().into_keypair().unwrap(); - /// # f(tsk, keypair).unwrap(); - /// # fn f(cert: Cert, mut signing_keypair: KeyPair) - /// # -> Result<()> { - /// let p = &StandardPolicy::new(); - /// - /// let mut o = vec![]; + /// let mut sink = vec![]; /// { - /// let message = Message::new(&mut o); - /// let signer = Signer::new(message, signing_keypair).build()?; - /// let mut ls = LiteralWriter::new(signer).build()?; - /// ls.write_all(b"Make it so, number one!")?; - /// ls.finalize()?; + /// let message = Message::new(&mut sink); + /// let message = Signer::new(message, signing_keypair) + /// // Customize the `Signer` here. + /// .build()?; + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(b"Make it so, number one!")?; + /// message.finalize()?; /// } /// /// // Now check the signature. @@ -288,13 +513,12 @@ impl<'a> Signer<'a> { /// } /// } /// - /// let mut verifier = Verifier::from_bytes(p, &o, Helper(&cert), None)?; + /// let mut verifier = Verifier::from_bytes(p, &sink, Helper(&cert), None)?; /// /// let mut message = String::new(); /// verifier.read_to_string(&mut message)?; /// assert_eq!(&message, "Make it so, number one!"); - /// # Ok(()) - /// # } + /// # Ok(()) } /// ``` pub fn new<S>(inner: Message<'a, Cookie>, signer: S) -> Self where S: crypto::Signer + 'a @@ -316,39 +540,22 @@ impl<'a> Signer<'a> { } } - /// Sets the hash algorithm to use for the signatures. - pub fn hash_algo(mut self, algo: HashAlgorithm) -> Result<Self> { - self.hash = algo.context()?; - Ok(self) - } - - /// Adds an additional signer. - pub fn add_signer<S>(mut self, signer: S) -> Self - where S: crypto::Signer + 'a - { - self.signers.push(Box::new(signer)); - self - } - - /// Adds an intended recipient. - /// - /// This signer emits signatures indicating the intended - /// recipients of the encryption container containing the - /// signature. This prevents forwarding a signed message using a - /// different encryption context. - pub fn add_intended_recipient(mut self, recipient: &Cert) -> Self { - self.intended_recipients.push(recipient.fingerprint()); - self - } - /// Creates a signer for a detached signature. /// + /// Changes the `Signer` to create a detached signature (see + /// [Section 11.4 of RFC 4880]). Note that the literal data *must + /// not* be wrapped using the [`LiteralWriter`]. + /// + /// [Section 11.4 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.4 + /// [`LiteralWriter`]: ../struct.LiteralWriter.html + /// /// # Example /// /// ``` - /// extern crate sequoia_openpgp as openpgp; - /// use std::io::{Read, Write}; - /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::serialize::stream::{Message, Signer}; /// use sequoia_openpgp::policy::StandardPolicy; /// # use openpgp::{Result, Cert}; /// # use openpgp::packet::prelude::*; @@ -356,25 +563,24 @@ impl<'a> Signer<'a> { /// # use openpgp::parse::Parse; /// # use openpgp::parse::stream::*; /// - /// # let p = &StandardPolicy::new(); - /// # let tsk = Cert::from_bytes(&include_bytes!( - /// # "../../tests/data/keys/testy-new-private.pgp")[..]) - /// # .unwrap(); - /// # let keypair - /// # = tsk.keys().with_policy(p, None).alive().revoked(false).for_signing() - /// # .nth(0).unwrap() - /// # .key().clone().parts_into_secret().unwrap().into_keypair() - /// # .unwrap(); - /// # f(tsk, keypair).unwrap(); - /// # fn f(cert: Cert, mut signing_keypair: KeyPair) - /// # -> Result<()> { /// let p = &StandardPolicy::new(); + /// # let cert = Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// # let signing_keypair + /// # = cert.keys().secret() + /// # .with_policy(p, None).alive().revoked(false).for_signing() + /// # .nth(0).unwrap() + /// # .key().clone().into_keypair()?; /// - /// let mut o = vec![]; + /// let mut sink = vec![]; /// { - /// let message = Message::new(&mut o); - /// let mut signer = - /// Signer::new(message, signing_keypair).detached().build()?; + /// let message = Message::new(&mut sink); + /// let mut signer = Signer::new(message, signing_keypair) + /// .detached() + /// // Customize the `Signer` here. + /// .build()?; + /// + /// // Write the data directly to the `Signer`. /// signer.write_all(b"Make it so, number one!")?; /// // In reality, just io::copy() the file to be signed. /// signer.finalize()?; @@ -400,21 +606,191 @@ impl<'a> Signer<'a> { /// } /// /// let mut verifier = - /// DetachedVerifier::from_bytes(p, &o, Helper(&cert), None)?; + /// DetachedVerifier::from_bytes(p, &sink, Helper(&cert), None)?; /// /// verifier.verify_bytes(b"Make it so, number one!")?; - /// # Ok(()) - /// # } + /// # Ok(()) } /// ``` pub fn detached(mut self) -> Self { self.detached = true; self } + /// Adds an additional signer. + /// + /// Can be used multiple times. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; + /// # use openpgp::policy::StandardPolicy; + /// # use openpgp::{Result, Cert}; + /// # use openpgp::packet::prelude::*; + /// # use openpgp::parse::Parse; + /// # use openpgp::parse::stream::*; + /// + /// # let p = &StandardPolicy::new(); + /// # let cert = Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// # let signing_keypair = cert.keys().secret() + /// # .with_policy(p, None).alive().revoked(false).for_signing() + /// # .nth(0).unwrap() + /// # .key().clone().into_keypair()?; + /// # let additional_signing_keypair = cert.keys().secret() + /// # .with_policy(p, None).alive().revoked(false).for_signing() + /// # .nth(0).unwrap() + /// # .key().clone().into_keypair()?; + /// + /// # let mut sink = vec![]; + /// let message = Message::new(&mut sink); + /// let message = Signer::new(message, signing_keypair) + /// .add_signer(additional_signing_keypair) + /// .build()?; + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(b"Make it so, number one!")?; + /// message.finalize()?; + /// # Ok(()) } + /// ``` + pub fn add_signer<S>(mut self, signer: S) -> Self + where S: crypto::Signer + 'a + { + self.signers.push(Box::new(signer)); + self + } + + /// Adds an intended recipient. + /// + /// Indicates that the given certificate is an intended recipient + /// of this message. Can be used multiple times. This prevents + /// [*Surreptitious Forwarding*] of encrypted and signed messages, + /// i.e. forwarding a signed message using a different encryption + /// context. + /// + /// [*Surreptitious Forwarding*]: http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; + /// # use openpgp::policy::StandardPolicy; + /// # use openpgp::{Result, Cert}; + /// # use openpgp::packet::prelude::*; + /// # use openpgp::crypto::KeyPair; + /// # use openpgp::parse::Parse; + /// # use openpgp::parse::stream::*; + /// + /// # let p = &StandardPolicy::new(); + /// # let cert = Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// # let signing_keypair = cert.keys().secret() + /// # .with_policy(p, None).alive().revoked(false).for_signing() + /// # .nth(0).unwrap() + /// # .key().clone().into_keypair()?; + /// let recipient: Cert = // ... + /// # Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy.pgp")[..])?; + /// + /// # let mut sink = vec![]; + /// let message = Message::new(&mut sink); + /// let message = Signer::new(message, signing_keypair) + /// .add_intended_recipient(&recipient) + /// .build()?; + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(b"Make it so, number one!")?; + /// message.finalize()?; + /// # Ok(()) } + /// ``` + pub fn add_intended_recipient(mut self, recipient: &Cert) -> Self { + self.intended_recipients.push(recipient.fingerprint()); + self + } + + /// Sets the hash algorithm to use for the signatures. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::types::HashAlgorithm; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; + /// # use openpgp::policy::StandardPolicy; + /// # use openpgp::{Result, Cert}; + /// # use openpgp::packet::prelude::*; + /// # use openpgp::parse::Parse; + /// # use openpgp::parse::stream::*; + /// + /// # let p = &StandardPolicy::new(); + /// # let cert = Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// # let signing_keypair = cert.keys().secret() + /// # .with_policy(p, None).alive().revoked(false).for_signing() + /// # .nth(0).unwrap() + /// # .key().clone().into_keypair()?; + /// + /// # let mut sink = vec![]; + /// let message = Message::new(&mut sink); + /// let message = Signer::new(message, signing_keypair) + /// .hash_algo(HashAlgorithm::SHA384)? + /// .build()?; + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(b"Make it so, number one!")?; + /// message.finalize()?; + /// # Ok(()) } + /// ``` + pub fn hash_algo(mut self, algo: HashAlgorithm) -> Result<Self> { + self.hash = algo.context()?; + Ok(self) + } + /// Sets the signature's creation time to `time`. /// /// Note: it is up to the caller to make sure the signing keys are /// actually valid as of `time`. + /// + /// # Example + /// + /// ``` + /// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> { + /// use std::io::Write; + /// use sequoia_openpgp as openpgp; + /// use openpgp::types::Timestamp; + /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter}; + /// use openpgp::policy::StandardPolicy; + /// # use openpgp::{Result, Cert}; + /// # use openpgp::packet::prelude::*; + /// # use openpgp::parse::Parse; + /// # use openpgp::parse::stream::*; + /// + /// let p = &StandardPolicy::new(); + /// let cert: Cert = // ... + /// # Cert::from_bytes(&include_bytes!( + /// # "../../tests/data/keys/testy-new-private.pgp")[..])?; + /// let signing_key = cert.keys().secret() + /// .with_policy(p, None).alive().revoked(false).for_signing() + /// .nth(0).unwrap() + /// .key(); + /// let signing_keypair = signing_key.clone().into_keypair()?; + /// + /// # let mut sink = vec![]; + /// let message = Message::new(&mut sink); + /// let message = Signer::new(message, signing_keypair) + /// .creation_time(Timestamp::now() + /// .round_down(None, signing_key.creation_time())?) + /// .build()?; + /// let mut message = LiteralWriter::new(message).build()?; + /// message.write_all(b"Make it so, number one!")?; + /// message.finalize()?; + /// # Ok(()) } + /// ``` pub fn creation_time<T: Into<SystemTime>>(mut self, creation_time: T) -> Self { @@ -423,6 +799,50 @@ impl<'a> Signer<'a> { } /// Finalizes the signer, returning the writer stack. + /// + /// The most useful filter to push to the writer stack next is the + /// [`LiteralWriter`]. Note, if you are creating a signed OpenPGP |