summaryrefslogtreecommitdiffstats
path: root/openpgp/src/serialize/stream.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-04-07 13:19:12 +0200
committerJustus Winter <justus@sequoia-pgp.org>2020-04-07 14:35:48 +0200
commitf6a00b3eee40fa92339d55c55b6806e9324152fa (patch)
treeb4f45cf41850f0f1c227bb09d739f0821b3ff3d4 /openpgp/src/serialize/stream.rs
parent38eab42143318a622759dc98a5cc5ecd5df9b9b4 (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.rs1477
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