//! Streaming packet serialization.
//!
//! This is the preferred interface to generate OpenPGP messages.  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.
//!
//! See the [encryption example].
//!
//! [encryption example]: struct.Encryptor.html#example

use std::fmt;
use std::io::{self, Write};
use std::iter;
use time;

use {
    crypto,
    Error,
    Fingerprint,
    HashAlgorithm,
    Result,
    crypto::Password,
    crypto::SessionKey,
    packet::prelude::*,
    packet::signature,
    TPK,
};
use packet::ctb::CTB;
use packet::BodyLength;
use super::{
    PartialBodyFilter,
    Serialize,
    writer,
};
use constants::{
    AEADAlgorithm,
    CompressionAlgorithm,
    DataFormat,
    SignatureType,
    SymmetricAlgorithm,
};
use conversions::Time;

/// Cookie must be public because the writers are.
#[doc(hidden)]
#[derive(Debug)]
pub struct Cookie {
    level: usize,
    private: Private,
}

#[derive(Debug)]
enum Private {
    Nothing,
    Signer,
}

impl Cookie {
    fn new(level: usize) -> Self {
        Cookie {
            level: level,
            private: Private::Nothing,
        }
    }
}

impl Default for Cookie {
    fn default() -> Self {
        Cookie::new(0)
    }
}

/// Streams an OpenPGP message.
///
/// Wraps a `std::io::Write`r for use with the streaming subsystem.
pub struct Message {
}

impl Message {
    /// Streams an OpenPGP message.
    pub fn new<'a, W: 'a + io::Write>(w: W) -> writer::Stack<'a, Cookie> {
        writer::Generic::new(w, Cookie::new(0))
    }
}

impl<'a> From<&'a mut io::Write> for writer::Stack<'a, Cookie> {
    fn from(w: &'a mut io::Write) -> Self {
        writer::Generic::new(w, Cookie::new(0))
    }
}


/// 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(())
/// # }
pub struct ArbitraryWriter<'a> {
    inner: writer::BoxStack<'a, Cookie>,
}

impl<'a> ArbitraryWriter<'a> {
    /// Creates a new writer with the given tag.
    pub fn new(mut inner: writer::Stack<'a, Cookie>, tag: Tag)
               -> Result<writer::Stack<'a, Cookie>> {
        let level = inner.as_ref().cookie_ref().level + 1;
        CTB::new(tag).serialize(&mut inner)?;
        Ok(writer::Stack::from(Box::new(ArbitraryWriter {
            inner: PartialBodyFilter::new(inner, Cookie::new(level)).into()
        })))
    }
}

impl<'a> fmt::Debug for ArbitraryWriter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("ArbitraryWriter")
            .field("inner", &self.inner)
            .finish()
    }
}

impl<'a> Write for ArbitraryWriter<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)
    }
    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl<'a> writer::Stackable<'a, Cookie> for ArbitraryWriter<'a> {
    fn into_inner(self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        Box::new(self.inner).into_inner()
    }
    fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        unreachable!("Only implemented by Signer")
    }
    /// Sets the inner stackable.
    fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
        unreachable!("Only implemented by Signer")
    }
    fn inner_ref(&self) -> Option<&writer::Stackable<'a, Cookie>> {
        self.inner.inner_ref()
    }
    fn inner_mut(&mut self) -> Option<&mut writer::Stackable<'a, Cookie>> {
        self.inner.inner_mut()
    }
    fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
        self.inner.cookie_set(cookie)
    }
    fn cookie_ref(&self) -> &Cookie {
        self.inner.cookie_ref()
    }
    fn cookie_mut(&mut self) -> &mut Cookie {
        self.inner.cookie_mut()
    }
}

/// Signs a packet stream.
///
/// 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.
///
/// Unless otherwise specified, SHA512 is used as hash algorithm.
pub struct Signer<'a> {
    // The underlying writer.
    //
    // Because this writer implements `Drop`, we cannot move the inner
    // writer out of this writer.  We therefore wrap it with `Option`
    // so that we can `take()` it.
    //
    // Furthermore, the LiteralWriter will pop us off the stack, and
    // take our inner reader.  If that happens, we only update the
    // digests.
    inner: Option<writer::BoxStack<'a, Cookie>>,
    signers: Vec<&'a mut dyn crypto::Signer>,
    intended_recipients: Option<Vec<Fingerprint>>,
    detached: bool,
    hash: crypto::hash::Context,
    cookie: Cookie,
}

impl<'a> Signer<'a> {
    /// Creates a signer.
    ///
    /// # Example
    ///
    /// ```
    /// extern crate sequoia_openpgp as openpgp;
    /// use std::io::{Read, Write};
    /// use openpgp::constants::DataFormat;
    /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter};
    /// # use openpgp::{Result, TPK};
    /// # use openpgp::packet::key::SecretKey;
    /// # use openpgp::crypto::KeyPair;
    /// # use openpgp::parse::Parse;
    /// # use openpgp::parse::stream::*;
    /// # let tsk = TPK::from_bytes(include_bytes!(
    /// #     "../../tests/data/keys/testy-new-private.pgp"))
    /// #     .unwrap();
    /// # let keypair = tsk.keys_valid().signing_capable().nth(0).unwrap().2
    /// #     .clone().into_keypair().unwrap();
    /// # f(tsk, keypair).unwrap();
    /// # fn f(tpk: TPK, mut signing_keypair: KeyPair) -> Result<()> {
    ///
    /// let mut o = vec![];
    /// {
    ///     let message = Message::new(&mut o);
    ///     let signer = Signer::new(message, vec![&mut signing_keypair], None)?;
    ///     let mut ls = LiteralWriter::new(signer, DataFormat::Text, None, None)?;
    ///     ls.write_all(b"Make it so, number one!")?;
    ///     ls.finalize()?;
    /// }
    ///
    /// // Now check the signature.
    /// struct Helper<'a>(&'a openpgp::TPK);
    /// impl<'a> VerificationHelper for Helper<'a> {
    ///     fn get_public_keys(&mut self, _: &[openpgp::KeyID])
    ///                        -> openpgp::Result<Vec<openpgp::TPK>> {
    ///         Ok(vec![self.0.clone()])
    ///     }
    ///
    ///     fn check(&mut self, structure: &MessageStructure)
    ///              -> openpgp::Result<()> {
    ///         if let MessageLayer::SignatureGroup { ref results } =
    ///             structure.iter().nth(0).unwrap()
    ///         {
    ///             if let VerificationResult::GoodChecksum(..) =
    ///                 results.get(0).unwrap()
    ///             { Ok(()) /* good */ } else { panic!() }
    ///         } else { panic!() }
    ///     }
    /// }
    ///
    /// let mut verifier = Verifier::from_bytes(&o, Helper(&tpk), None)?;
    ///
    /// let mut message = String::new();
    /// verifier.read_to_string(&mut message)?;
    /// assert_eq!(&message, "Make it so, number one!");
    /// # Ok(())
    /// # }
    /// ```
    pub fn new<H>(inner: writer::Stack<'a, Cookie>,
                  signers: Vec<&'a mut dyn crypto::Signer>,
                  hash_algo: H)
                  -> Result<writer::Stack<'a, Cookie>>
        where H: Into<Option<HashAlgorithm>>
    {
        Self::make(inner, signers, None, false, hash_algo)
    }

    /// Creates a signer with intended recipients.
    ///
    /// 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 with_intended_recipients<H>(inner: writer::Stack<'a, Cookie>,
                                       signers: Vec<&'a mut dyn crypto::Signer>,
                                       recipients: &[&'a TPK],
                                       hash_algo: H)
                                       -> Result<writer::Stack<'a, Cookie>>
        where H: Into<Option<HashAlgorithm>>
    {
        Self::make(inner, signers,
                   Some(recipients.iter().map(|r| r.fingerprint()).collect()),
                   false, hash_algo)
    }

    /// Creates a signer for a detached signature.
    ///
    /// # Example
    ///
    /// ```
    /// extern crate sequoia_openpgp as openpgp;
    /// use std::io::{Read, Write};
    /// use openpgp::serialize::stream::{Message, Signer, LiteralWriter};
    /// # use openpgp::{Result, TPK};
    /// # use openpgp::packet::key::SecretKey;
    /// # use openpgp::crypto::KeyPair;
    /// # use openpgp::parse::Parse;
    /// # use openpgp::parse::stream::*;
    /// # let tsk = TPK::from_bytes(include_bytes!(
    /// #     "../../tests/data/keys/testy-new-private.pgp"))
    /// #     .unwrap();
    /// # let keypair = tsk.keys_valid().signing_capable().nth(0).unwrap().2
    /// #     .clone().into_keypair().unwrap();
    /// # f(tsk, keypair).unwrap();
    /// # fn f(tpk: TPK, mut signing_keypair: KeyPair) -> Result<()> {
    ///
    /// let mut o = vec![];
    /// {
    ///     let message = Message::new(&mut o);
    ///     let mut signer =
    ///         Signer::detached(message, vec![&mut signing_keypair], None)?;
    ///     signer.write_all(b"Make it so, number one!")?;
    ///     // In reality, just io::copy() the file to be signed.
    ///     signer.finalize()?;
    /// }
    ///
    /// // Now check the signature.
    /// struct Helper<'a>(&'a openpgp::TPK);
    /// impl<'a> VerificationHelper for Helper<'a> {
    ///     fn get_public_keys(&mut self, _: &[openpgp::KeyID])
    ///                        -> openpgp::Result<Vec<openpgp::TPK>> {
    ///         Ok(vec![self.0.clone()])
    ///     }
    ///
    ///     fn check(&mut self, structure: &MessageStructure)
    ///              -> openpgp::Result<()> {
    ///         if let MessageLayer::SignatureGroup { ref results } =
    ///             structure.iter().nth(0).unwrap()
    ///         {
    ///             if let VerificationResult::GoodChecksum(..) =
    ///                 results.get(0).unwrap()
    ///             { Ok(()) /* good */ } else { panic!() }
    ///         } else { panic!() }
    ///     }
    /// }
    ///
    /// let mut verifier =
    ///     DetachedVerifier::from_bytes(&o, b"Make it so, number one!",
    ///                                  Helper(&tpk), None)?;
    ///
    /// let mut message = String::new();
    /// verifier.read_to_string(&mut message)?;
    /// assert_eq!(&message, "Make it so, number one!");
    /// # Ok(())
    /// # }
    /// ```
    pub fn detached<H>(inner: writer::Stack<'a, Cookie>,
                       signers: Vec<&'a mut dyn crypto::Signer>,
                       hash_algo: H)
                       -> Result<writer::Stack<'a, Cookie>>
        where H: Into<Option<HashAlgorithm>>
    {
        Self::make(inner, signers, None, true, hash_algo)
    }

    fn make<H>(inner: writer::Stack<'a, Cookie>,
               signers: Vec<&'a mut dyn crypto::Signer>,
               intended_recipients: Option<Vec<Fingerprint>>, detached: bool,
               hash_algo: H)
               -> Result<writer::Stack<'a, Cookie>>
        where H: Into<Option<HashAlgorithm>>
    {
        let mut inner = writer::BoxStack::from(inner);
        let hash_algo = hash_algo.into().unwrap_or(HashAlgorithm::SHA512);

        if signers.len() == 0 {
            return Err(Error::InvalidArgument(
                "No signing keys given".into()).into());
        }

        if ! detached {
            // For every key we collected, build and emit a one pass
            // signature packet.
            for (i, keypair) in signers.iter().enumerate() {
                let key = keypair.public();
                let mut ops = OnePassSig3::new(SignatureType::Binary);
                ops.set_pk_algo(key.pk_algo());
                ops.set_hash_algo(hash_algo);
                ops.set_issuer(key.keyid());
                ops.set_last(i == signers.len() - 1);
                Packet::OnePassSig(ops.into()).serialize(&mut inner)?;
            }
        }

        let level = inner.cookie_ref().level + 1;
        Ok(writer::Stack::from(Box::new(Signer {
            inner: Some(inner),
            signers: signers,
            intended_recipients: intended_recipients,
            detached: detached,
            hash: hash_algo.context()?,
            cookie: Cookie {
                level: level,
                private: Private::Signer,
            },
        })))
    }

    fn emit_signatures(&mut self) -> Result<()> {
        if let Some(ref mut sink) = self.inner {
            // Emit the signatures in reverse, so that the
            // one-pass-signature and signature packets "bracket" the
            // message.
            for signer in self.signers.iter_mut() {
                // Part of the signature packet is hashed in,
                // therefore we need to clone the hash.
                let mut hash = self.hash.clone();

                // Make and hash a signature packet.
                let mut sig = signature::Builder::new(SignatureType::Binary)
                    .set_signature_creation_time(time::now().canonicalize())?
                    .set_issuer_fingerprint(signer.public().fingerprint())?
                    // GnuPG up to (and including) 2.2.8 requires the
                    // Issuer subpacket to be present.
                    .set_issuer(signer.public().keyid())?;

                if let Some(ref ir) = self.intended_recipients {
                    sig = sig.set_intended_recipients(ir.clone())?;
                }

                // Compute the signature.
                let sig = sig.sign_hash(*signer, HashAlgorithm::SHA512, hash)?;

                // And emit the packet.
                Packet::Signature(sig).serialize(sink)?;
            }
        }
        Ok(())
    }
}

impl<'a> Drop for Signer<'a> {
    fn drop(&mut self) {
        let _ = self.emit_signatures();
    }
}

impl<'a> fmt::Debug for Signer<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Signer")
            .field("inner", &self.inner)
            .field("cookie", &self.cookie)
            .finish()
    }
}

impl<'a> Write for Signer<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let written = match self.inner.as_mut() {
            // If we are creating a normal signature, pass data
            // through.
            Some(ref mut w) if ! self.detached => w.write(buf),
            // If we are creating a detached signature, just hash all
            // bytes.
            Some(_) => Ok(buf.len()),
            // When we are popped off the stack, we have no inner
            // writer.  Just hash all bytes.
            None => Ok(buf.len()),
        };

        if let Ok(amount) = written {
            self.hash.update(&buf[..amount]);
        }

        written
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.inner.as_mut() {
            Some(ref mut w) => w.flush(),
            // When we are popped off the stack, we have no inner
            // writer.  Just do nothing.
            None => Ok(()),
        }
    }
}

impl<'a> writer::Stackable<'a, Cookie> for Signer<'a> {
    fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        Ok(self.inner.take())
    }
    fn mount(&mut self, new: writer::BoxStack<'a, Cookie>) {
        self.inner = Some(new);
    }
    fn inner_mut(&mut self) -> Option<&mut writer::Stackable<'a, Cookie>> {
        if let Some(ref mut i) = self.inner {
            Some(i)
        } else {
            None
        }
    }
    fn inner_ref(&self) -> Option<&writer::Stackable<'a, Cookie>> {
        if let Some(ref i) = self.inner {
            Some(i)
        } else {
            None
        }
    }
    fn into_inner(mut self: Box<Self>)
                  -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        self.emit_signatures()?;
        Ok(self.inner.take())
    }
    fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
        ::std::mem::replace(&mut self.cookie, cookie)
    }
    fn cookie_ref(&self) -> &Cookie {
        &self.cookie
    }
    fn cookie_mut(&mut self) -> &mut Cookie {
        &mut self.cookie
    }
}


/// Writes a literal data packet.
///
/// 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::constants::DataFormat;
/// use openpgp::serialize::stream::{Message, LiteralWriter};
/// # use openpgp::Result;
/// # f().unwrap();
/// # fn f() -> Result<()> {
///
/// let mut o = vec![];
/// {
///     let message = Message::new(&mut o);
///     let mut w = LiteralWriter::new(message, DataFormat::Text, None, None)?;
///     w.write_all(b"Hello world.")?;
///     w.finalize()?;
/// }
/// assert_eq!(b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.", o.as_slice());
/// # Ok(())
/// # }
/// ```
pub struct LiteralWriter<'a> {
    inner: writer::BoxStack<'a, Cookie>,
    signature_writer: Option<writer::BoxStack<'a, Cookie>>,
}

impl<'a> LiteralWriter<'a> {
    /// Creates a new literal writer.
    ///
    /// `format`, `filename`, and `date` will be emitted as part of
    /// the literal packets headers.  Note that these headers will not
    /// be authenticated by signatures (but will be authenticated by a
    /// SEIP/MDC container), and are therefore unreliable and should
    /// not be trusted.
    ///
    /// If `date` is `None`, then the earliest representable time will
    /// be used as a dummy value.
    pub fn new(inner: writer::Stack<'a, Cookie>,
               format: DataFormat,
               filename: Option<&[u8]>,
               date: Option<time::Tm>)
               -> Result<writer::Stack<'a, Cookie>> {
        let mut inner = writer::BoxStack::from(inner);
        let level = inner.cookie_ref().level + 1;

        let mut template = Literal::new(format);
        template.set_date(date);

        if let Some(f) = filename {
            template.set_filename_from_bytes(f)?;
        }

        // For historical reasons, signatures over literal data
        // packets only include the body without metadata or framing.
        // Therefore, we check whether the writer is a
        // Signer, and if so, we pop it off the stack and
        // store it in 'self.signature_writer'.
        let signer_above =
            if let &Cookie {
                private: Private::Signer{..},
                ..
            } = inner.cookie_ref() {
                true
            } else {
                false
            };

        let mut signature_writer = None;
        if signer_above {
            let stack = inner.pop()?;
            // We know a signer has an inner stackable.
            let stack = stack.unwrap();
            signature_writer = Some(inner);
            inner = stack;
        }

        // Not hashed by the signature_writer (see above).
        CTB::new(Tag::Literal).serialize(&mut inner)?;

        // Neither is any framing added by the PartialBodyFilter.
        let mut inner
            = PartialBodyFilter::new(writer::Stack::from(inner), Cookie::new(level));

        // Nor the headers.
        template.serialize_headers(&mut inner, false)?;

        Ok(writer::Stack::from(Box::new(Self {
            inner: inner.into(),
            signature_writer: signature_writer,
        })))
    }
}

impl<'a> fmt::Debug for LiteralWriter<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("LiteralWriter")
            .field("inner", &self.inner)
            .finish()
    }
}

impl<'a> Write for LiteralWriter<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let written = self.inner.write(buf);

        // Any successful written bytes needs to be hashed too.
        if let (&Ok(ref amount), &mut Some(ref mut sig))
            = (&written, &mut self.signature_writer) {
                sig.write_all(&buf[..*amount])?;
            };
        written
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl<'a> writer::Stackable<'a, Cookie> for LiteralWriter<'a> {
    fn into_inner(mut self: Box<Self>)
                  -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        let signer = self.signature_writer.take();
        let stack = self.inner
            .into_inner()?.unwrap(); // Peel off the PartialBodyFilter.

        if let Some(mut signer) = signer {
            // We stashed away a Signer.  Reattach it to the
            // stack and return it.
            signer.mount(stack);
            Ok(Some(signer))
        } else {
            Ok(Some(stack))
        }
    }

    fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        unreachable!("Only implemented by Signer")
    }
    /// Sets the inner stackable.
    fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
        unreachable!("Only implemented by Signer")
    }
    fn inner_ref(&self) -> Option<&writer::Stackable<'a, Cookie>> {
        self.inner.inner_ref()
    }
    fn inner_mut(&mut self) -> Option<&mut writer::Stackable<'a, Cookie>> {
        self.inner.inner_mut()
    }
    fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
        self.inner.cookie_set(cookie)
    }
    fn cookie_ref(&self) -> &Cookie {
        self.inner.cookie_ref()
    }
    fn cookie_mut(&mut self) -> &mut Cookie {
        self.inner.cookie_mut()
    }
}

/// Compresses a packet stream.
///
/// Writes a compressed data packet containing all packets written to
/// this writer.
///
/// # Example
///
/// ```
/// extern crate sequoia_openpgp as openpgp;
/// use std::io::Write;
/// use openpgp::constants::DataFormat;
/// use openpgp::serialize::stream::{Message, Compressor, LiteralWriter};
/// use openpgp::constants::CompressionAlgorithm;
/// # use openpgp::Result;
/// # f().unwrap();
/// # fn f() -> Result<()> {
///
/// let mut o = vec![];
/// {
///     let message = Message::new(&mut o);
///     let w = Compressor::new(message,
///                             CompressionAlgorithm::Uncompressed)?;
///     let mut w = LiteralWriter::new(w, DataFormat::Text, None, None)?;
///     w.write_all(b"Hello world.")?;
///     w.finalize()?;
/// }
/// assert_eq!(b"\xc8\x15\x00\xcb\x12t\x00\x00\x00\x00\x00Hello world.",
///            o.as_slice());
/// # Ok(())
/// # }
pub struct Compressor<'a> {
    inner: writer::BoxStack<'a, Cookie>,
}

impl<'a> Compressor<'a> {
    /// Creates a new compressor using the given algorithm.
    pub fn new(inner: writer::Stack<'a, Cookie>, algo: CompressionAlgorithm)
               -> Result<writer::Stack<'a, Cookie>> {
        let mut inner = writer::BoxStack::from(inner);
        let level = inner.cookie_ref().level + 1;

        // Packet header.
        CTB::new(Tag::CompressedData).serialize(&mut inner)?;
        let inner: writer::Stack<'a, Cookie>
            = PartialBodyFilter::new(writer::Stack::from(inner),
                                     Cookie::new(level));

        Self::new_naked(inner, algo, level)
    }


    /// Creates a new compressor using the given algorithm.
    pub(crate) // For CompressedData::serialize.
        fn new_naked(mut inner: writer::Stack<'a, Cookie>, algo: CompressionAlgorithm,
                     level: usize)
                 -> Result<writer::Stack<'a, Cookie>> {
        // Compressed data header.
        inner.as_mut().write_u8(algo.into())?;

        // Create an appropriate filter.
        let inner: writer::Stack<'a, Cookie> = match algo {
            CompressionAlgorithm::Uncompressed =>
                writer::Identity::new(inner, Cookie::new(level)),
            #[cfg(feature = "compression-deflate")]
            CompressionAlgorithm::Zip =>
                writer::ZIP::new(inner, Cookie::new(level)),
            #[cfg(feature = "compression-deflate")]
            CompressionAlgorithm::Zlib =>
                writer::ZLIB::new(inner, Cookie::new(level)),
            #[cfg(feature = "compression-bzip2")]
            CompressionAlgorithm::BZip2 =>
                writer::BZ::new(inner, Cookie::new(level)),
            a =>
                return Err(Error::UnsupportedCompressionAlgorithm(a).into()),
        };

        Ok(writer::Stack::from(Box::new(Self{inner: inner.into()})))
    }
}

impl<'a> fmt::Debug for Compressor<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Compressor")
            .field("inner", &self.inner)
            .finish()
    }
}

impl<'a> io::Write for Compressor<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl<'a> writer::Stackable<'a, Cookie> for Compressor<'a> {
    fn into_inner(self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        Box::new(self.inner).into_inner()?.unwrap().into_inner()
    }
    fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        unreachable!("Only implemented by Signer")
    }
    /// Sets the inner stackable.
    fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
        unreachable!("Only implemented by Signer")
    }
    fn inner_ref(&self) -> Option<&writer::Stackable<'a, Cookie>> {
        self.inner.inner_ref()
    }
    fn inner_mut(&mut self) -> Option<&mut writer::Stackable<'a, Cookie>> {
        self.inner.inner_mut()
    }
    fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
        self.inner.cookie_set(cookie)
    }
    fn cookie_ref(&self) -> &Cookie {
        self.inner.cookie_ref()
    }
    fn cookie_mut(&mut self) -> &mut Cookie {
        self.inner.cookie_mut()
    }
}

/// Encrypts a packet stream.
pub struct Encryptor<'a> {
    inner: Option<writer::BoxStack<'a, Cookie>>,
    hash: crypto::hash::Context,
    cookie: Cookie,
}

/// Specifies whether to encrypt for archival purposes or for
/// transport.
pub enum EncryptionMode {
    /// Encrypt data for long-term storage.
    ///
    /// This should be used for things that should be decryptable for
    /// a long period of time, e.g. backups, archives, etc.
    AtRest,

    /// Encrypt data for transport.
    ///
    /// This should be used to protect a message in transit.  The
    /// recipient is expected to take additional steps if she wants to
    /// be able to decrypt it later on, e.g. store the decrypted
    /// session key, or re-encrypt the session key with a different
    /// key.
    ForTransport,
}

impl<'a> Encryptor<'a> {
    /// Creates a new encryptor.
    ///
    /// The stream will be encrypted using a generated session key,
    /// which will be encrypted using the given passwords, and all
    /// encryption-capable subkeys of the given TPKs.
    ///
    /// Unless otherwise specified, the stream is encrypted using
    /// AES256.  Key preferences of the recipients are not honored.
    ///
    /// # Example
    ///
    /// ```
    /// use std::io::Write;
    /// extern crate sequoia_openpgp as openpgp;
    /// use openpgp::constants::DataFormat;
    /// use openpgp::serialize::stream::{
    ///     Message, Encryptor, EncryptionMode, LiteralWriter,
    /// };
    /// # use openpgp::Result;
    /// # use openpgp::parse::Parse;
    /// # fn main() { f().unwrap(); }
    /// # fn f() -> Result<()> {
    /// let tpk = openpgp::TPK::from_bytes(
    /// #   // We do some acrobatics here to abbreviate the TPK.
    ///    b"-----BEGIN PGP PUBLIC KEY BLOCK-----
    ///
    ///      mQENBFpxtsABCADZcBa1Q3ZLZnju18o0+t8LoQuIIeyeUQ0H45y6xUqyrD5HSkVM
    /// #    VGQs6IHLq70mAizBJ4VznUVqVOh/NhOlapXi6/TKpjHvttdg45o6Pgqa0Kx64luT
    /// #    ZY+TEKyILcdBdhr3CzsEILnQst5jadgMvU9fnT/EkJIvxtWPlUzU5R7nnALO626x
    /// #    2M5Pj3k0h3ZNHMmYQQtReX/RP/xUh2SfOYG6i/MCclIlee8BXHB9k0bW2NAX2W7H
    /// #    rLDGPm1LzmyqxFGDvDvfPlYZ5nN2cbGsv3w75LDzv75kMhVnkZsrUjnHjVRzFq7q
    /// #    fSIpxlvJMEMKSIJ/TFztQoOBO5OlBb5qzYPpABEBAAG0F+G8iM+BzrnPg8+Ezr/P
    /// #    hM6tzrvOt8+CiQFUBBMBCAA+FiEEfcpYtU6xQxad3uFfJH9tq8hJFP4FAlpxtsAC
    /// #    GwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQJH9tq8hJFP49hgf+
    /// #    IKvec0RkD9EHSLFc6AKDm/knaI4AIH0isZTz9jRCF8H/j3h8QVUE+/0jtCcyvR6F
    /// #    TGVSfO3pelDPYGIjDFI3aA6H/UlhZWzYRXZ+QQRrV0zwvLna3XjiW8ib3Ky+5bpQ
    /// #    0uVeee30u+U3SnaCL9QB4+UvwVvAxRuk49Z0Q8TsRrQyQNYpeZDN7uNrvA134cf6
    /// #    6pLUvzPG4lMLIvSXFuHou704EhT7NS3wAzFtjMrsLLieVqtbEi/kBaJTQSZQwjVB
    /// #    sE/Z8lp1heKw/33Br3cB63n4cTf0FdoFywDBhCAMU7fKboU5xBpm5bQJ4ck6j6w+
    /// #    BKG1FiQRR6PCUeb6GjxVOrkBDQRacbbAAQgAw538MMb/pRdpt7PTgBCedw+rU9fh
    /// #    onZYKwmCO7wz5VrVf8zIVvWKxhX6fBTSAy8mxaYbeL/3woQ9Leuo8f0PQNs9zw1N
    /// #    mdH+cnm2KQmL9l7/HQKMLgEAu/0C/q7ii/j8OMYitaMUyrwy+OzW3nCal/uJHIfj
    /// #    bdKx29MbKgF/zaBs8mhTvf/Tu0rIVNDPEicwijDEolGSGebZxdGdHJA31uayMHDK
    /// #    /mwySJViMZ8b+Lzc/dRgNbQoY6yjsjso7U9OZpQK1fooHOSQS6iLsSSsZLcGPD+7
    /// #    m7j3jwq68SIJPMsu0O8hdjFWL4Cfj815CwptAxRGkp00CIusAabO7m8DzwARAQAB
    /// #    iQE2BBgBCAAgFiEEfcpYtU6xQxad3uFfJH9tq8hJFP4FAlpxtsACGwwACgkQJH9t
    /// #    q8hJFP5rmQgAoYOUXolTiQmWipJTdMG/VZ5X7mL8JiBWAQ11K1o01cZCMlziyHnJ
    /// #    xJ6Mqjb6wAFpYBtqysJG/vfjc/XEoKgfFs7+zcuEnt41xJQ6tl/L0VTxs+tEwjZu
    /// #    Rp/owB9GCkqN9+xNEnlH77TLW1UisW+l0F8CJ2WFOj4lk9rcXcLlEdGmXfWIlVCb
    /// #    2/o0DD+HDNsF8nWHpDEy0mcajkgIUTvXQaDXKbccX6Wgep8dyBP7YucGmRPd9Z6H
    /// #    bGeT3KvlJlH5kthQ9shsmT14gYwGMR6rKpNUXmlpetkjqUK7pGVaHGgJWUZ9QPGU
    /// #    awwPdWWvZSyXJAPZ9lC5sTKwMJDwIxILug==
    /// #    =lAie
    /// #    -----END PGP PUBLIC KEY BLOCK-----"
    /// #    /*
    ///      ...
    ///      -----END PGP PUBLIC KEY BLOCK-----"
    /// #    */
    /// ).unwrap();
    ///
    /// let mut o = vec![];
    /// let message = Message::new(&mut o);
    /// let encryptor = Encryptor::new(message,
    ///                                &[&"совершенно секретно".into()],
    ///                                &[&tpk], EncryptionMode::AtRest, None)
    ///     .expect("Failed to create encryptor");
    /// let mut w = LiteralWriter::new(encryptor, DataFormat::Text, None, None)?;
    /// w.write_all(b"Hello world.")?;
    /// w.finalize()?;
    /// # Ok(())
    /// # }
    /// ```
    pub fn new<C>(mut inner: writer::Stack<'a, Cookie>,
                  passwords: &[&Password], tpks: &[&TPK],
                  encryption_mode: EncryptionMode,
                  cipher_algo: C)
                  -> Result<writer::Stack<'a, Cookie>>
        where C: Into<Option<SymmetricAlgorithm>>
    {
        if tpks.len() + passwords.len() == 0 {
            return Err(Error::InvalidArgument(
                "Neither recipient keys nor passwords given".into()).into());
        }

        struct AEADParameters {
            algo: AEADAlgorithm,
            chunk_size: usize,
            nonce: Box<[u8]>,
        }

        // Use AEAD if there are TPKs and all of them support AEAD.
        let aead = if tpks.len() > 0 && tpks.iter().all(|t| {
            t.primary_key_signature().map(|s| s.features().supports_aead())
                .unwrap_or(false)
        }) {
            let mut nonce = vec![0; AEADAlgorithm::EAX.iv_size()?];
            crypto::random(&mut nonce);
            Some(AEADParameters {
                algo: AEADAlgorithm::EAX, // Must implement EAX.
                chunk_size: 4096, // A page, 3 per mille overhead.
                nonce: nonce.into_boxed_slice(),
            })
        } else {
            None
        };

        let level = inner.as_ref().cookie_ref().level + 1;
        let algo = cipher_algo.into().unwrap_or(SymmetricAlgorithm::AES256);

        // Generate a session key.
        let sk = SessionKey::new(algo.key_size()?);

        // Write the PKESK packet(s).
        for tpk in tpks {
            // We need to find all applicable encryption (sub)keys.
            let can_encrypt = |key: &Key, sig: Option<&Signature>| -> bool {
                if let Some(sig) = sig {
                    (match encryption_mode {
                        EncryptionMode::AtRest =>
                            sig.key_flags().can_encrypt_at_rest(),
                        EncryptionMode::ForTransport =>
                            sig.key_flags().can_encrypt_for_transport(),
                    }
                     // Check expiry.
                     && sig.signature_alive()
                     && sig.key_alive(key))
                } else {
                    false
                }
            };

            // Gather all encryption-capable subkeys.
            let subkeys = tpk.subkeys().filter_map(|skb| {
                let key = skb.subkey();
                if can_encrypt(key, skb.binding_signature()) {
                    Some(key)
                } else {
                    None
                }
            });

            // Check if the primary key is encryption-capable.
            let primary_can_encrypt =
                can_encrypt(tpk.primary(), tpk.primary_key_signature());

            // If the primary key is encryption-capable, prepend to
            // subkeys via iterator magic.
            let keys =
                iter::once(tpk.primary())
                .filter(|_| primary_can_encrypt)
                .chain(subkeys);

            let mut count = 0;
            for key in keys {
                if let Ok(pkesk) = PKESK3::for_recipient(algo, &sk, key) {
                    Packet::PKESK(pkesk.into()).serialize(&mut inner)?;
                    count += 1;
                }
            }

            if count == 0 {
                return Err(Error::InvalidOperation(
                    format!("Key {} has no suitable encryption subkey",
                            tpk)).into());
            }
        }

        // Write the SKESK packet(s).
        for password in passwords {
            if let Some(aead) = aead.as_ref() {
                let skesk = SKESK5::with_password(algo, aead.algo,
                                                  Default::default(),
                                                  &sk, password).unwrap();
                Packet::SKESK(skesk.into()).serialize(&mut inner)?;
            } else {
                let skesk = SKESK4::with_password(algo, Default::default(),
                                                  &sk, password).unwrap();
                Packet::SKESK(skesk.into()).serialize(&mut inner)?;
            }
        }

        let encryptor = if let Some(aead) = aead {
            // Write the AED packet.
            CTB::new(Tag::AED).serialize(&mut inner)?;
            let mut inner = PartialBodyFilter::new(inner, Cookie::new(level));
            let aed = AED1::new(algo, aead.algo, aead.chunk_size, aead.nonce)?;
            aed.serialize_headers(&mut inner)?;

            writer::AEADEncryptor::new(
                inner.into(),
                Cookie::new(level),
                aed.symmetric_algo(),
                aed.aead(),
                aed.chunk_size(),
                aed.iv(),
                &sk,
            )?
        } else {
            // Write the SEIP packet.
            CTB::new(Tag::SEIP).serialize(&mut inner)?;
            let mut inner = PartialBodyFilter::new(inner, Cookie::new(level));
            inner.write_all(&[1])?; // Version.

            let encryptor = writer::Encryptor::new(
                inner.into(),
                Cookie::new(level),
                algo,
                &sk,
            )?;

            // The hash for the MDC must include the initialization
            // vector, hence we build the object here.
            let mut encryptor = writer::Stack::from(Box::new(Self{
                inner: Some(encryptor.into()),
                hash: HashAlgorithm::SHA1.context().unwrap(),
                cookie: Cookie::new(level),
            }));

            // Write the initialization vector, and the quick-check bytes.
            let mut iv = vec![0; algo.block_size()?];
            crypto::random(&mut iv);
            encryptor.write_all(&iv)?;
            encryptor.write_all(&iv[iv.len() - 2..])?;

            encryptor
        };

        Ok(encryptor)
    }

    /// Emits the MDC packet and recovers the original writer.
    fn emit_mdc(&mut self) -> Result<writer::BoxStack<'a, Cookie>> {
        if let Some(mut w) = self.inner.take() {
            // Write the MDC, which must be the last packet inside the
            // encrypted packet stream.  The hash includes the MDC's
            // CTB and length octet.
            let mut header = Vec::new();
            CTB::new(Tag::MDC).serialize(&mut header)?;
            BodyLength::Full(20).serialize(&mut header)?;

            self.hash.update(&header);
            Packet::MDC(MDC::from(self.hash.clone())).serialize(&mut w)?;

            // Now recover the original writer.  First, strip the
            // Encryptor.
            let mut w = w.into_inner()?.unwrap();
            // And the partial body filter.
            let mut w = w.into_inner()?.unwrap();

            Ok(w)
        } else {
            Err(Error::InvalidOperation(
                "Inner writer already taken".into()).into())
        }
    }
}

impl<'a> Drop for Encryptor<'a> {
    fn drop(&mut self) {
        let _ = self.emit_mdc();
    }
}

impl<'a> fmt::Debug for Encryptor<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Encryptor")
            .field("inner", &self.inner)
            .finish()
    }
}

impl<'a> Write for Encryptor<'a> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let written = match self.inner.as_mut() {
            Some(ref mut w) => w.write(buf),
            None => Ok(buf.len()),
        };
        if let Ok(amount) = written {
            self.hash.update(&buf[..amount]);
        }
        written
    }

    fn flush(&mut self) -> io::Result<()> {
        match self.inner.as_mut() {
            Some(ref mut w) => w.flush(),
            None => Ok(()),
        }
    }
}

impl<'a> writer::Stackable<'a, Cookie> for Encryptor<'a> {
    fn pop(&mut self) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        unreachable!("Only implemented by Signer")
    }
    /// Sets the inner stackable.
    fn mount(&mut self, _new: writer::BoxStack<'a, Cookie>) {
        unreachable!("Only implemented by Signer")
    }
    fn inner_ref(&self) -> Option<&writer::Stackable<'a, Cookie>> {
        if let Some(ref i) = self.inner {
            Some(i)
        } else {
            None
        }
    }
    fn inner_mut(&mut self) -> Option<&mut writer::Stackable<'a, Cookie>> {
        if let Some(ref mut i) = self.inner {
            Some(i)
        } else {
            None
        }
    }
    fn into_inner(mut self: Box<Self>) -> Result<Option<writer::BoxStack<'a, Cookie>>> {
        Ok(Some(self.emit_mdc()?))
    }
    fn cookie_set(&mut self, cookie: Cookie) -> Cookie {
        ::std::mem::replace(&mut self.cookie, cookie)
    }
    fn cookie_ref(&self) -> &Cookie {
        &self.cookie
    }
    fn cookie_mut(&mut self) -> &mut Cookie {
        &mut self.cookie
    }
}

#[cfg(test)]
mod test {
    use std::io::Read;
    use {Packet, PacketPile, packet::CompressedData};
    use parse::{Parse, PacketParserResult, PacketParser};
    use super::*;
    use constants::DataFormat::Text as T;

    #[test]
    fn arbitrary() {
        let mut o = vec![];
        {
            let m = Message::new(&mut o);
            let mut ustr = ArbitraryWriter::new(m, Tag::Literal).unwrap();
            ustr.write_all(b"t").unwrap(); // type
            ustr.write_all(b"\x00").unwrap(); // fn length
            ustr.write_all(b"\x00\x00\x00\x00").unwrap(); // date
            ustr.write_all(b"Hello world.").unwrap(); // body
        }

        let mut pp = PacketParser::from_bytes(&o).unwrap().unwrap();
        if let Packet::Literal(ref l) = pp.packet {
                assert_eq!(l.format(), DataFormat::Text);
                assert_eq!(l.filename(), None);
                assert_eq!(l.date(), None);
        } else {
            panic!("Unexpected packet type.");
        }

        let mut body = vec![];
        pp.read_to_end(&mut body).unwrap();
        assert_eq!(&body, b"Hello world.");

        // Make sure it is the only packet.
        let (_, ppr) = pp.recurse().unwrap();
        assert!(ppr.is_none());
    }

    // Create some crazy nesting structures, serialize the messages,
    // reparse them, and make sure we get the same result.
    #[test]
    fn stream_0() {
        // 1: CompressedData(CompressedData { algo: 0 })
        //  1: Literal(Literal { body: "one (3 bytes)" })
        //  2: Literal(Literal { body: "two (3 bytes)" })
        // 2: Literal(Literal { body: "three (5 bytes)" })
        let mut one = Literal::new(T);
        one.set_body(b"one".to_vec());
        let mut two = Literal::new(T);
        two.set_body(b"two".to_vec());
        let mut three = Literal::new(T);
        three.set_body(b"three".to_vec());
        let mut reference = Vec::new();
        reference.push(
            CompressedData::new(CompressionAlgorithm::Uncompressed)
                .push(one.into())
                .push(two.into())
                .into());
        reference.push(three.into());

        let mut o = vec![];
        {
            let m = Message::new(&mut o);
            let c = Compressor::new(
                m, CompressionAlgorithm::Uncompressed).unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "one").unwrap();
            let c = ls.finalize_one().unwrap().unwrap(); // Pop the LiteralWriter.
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "two").unwrap();
            let c = ls.finalize_one().unwrap().unwrap(); // Pop the LiteralWriter.
            let c = c.finalize_one().unwrap().unwrap(); // Pop the Compressor.
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "three").unwrap();
        }

        let pile = PacketPile::from(reference);
        let pile2 = PacketPile::from_bytes(&o).unwrap();
        if pile != pile2 {
            eprintln!("REFERENCE...");
            pile.pretty_print();
            eprintln!("REPARSED...");
            pile2.pretty_print();
            panic!("Reparsed packet does not match reference packet!");
        }
    }

    // Create some crazy nesting structures, serialize the messages,
    // reparse them, and make sure we get the same result.
    #[test]
    fn stream_1() {
        // 1: CompressedData(CompressedData { algo: 0 })
        //  1: CompressedData(CompressedData { algo: 0 })
        //   1: Literal(Literal { body: "one (3 bytes)" })
        //   2: Literal(Literal { body: "two (3 bytes)" })
        //  2: CompressedData(CompressedData { algo: 0 })
        //   1: Literal(Literal { body: "three (5 bytes)" })
        //   2: Literal(Literal { body: "four (4 bytes)" })
        let mut one = Literal::new(T);
        one.set_body(b"one".to_vec());
        let mut two = Literal::new(T);
        two.set_body(b"two".to_vec());
        let mut three = Literal::new(T);
        three.set_body(b"three".to_vec());
        let mut four = Literal::new(T);
        four.set_body(b"four".to_vec());
        let mut reference = Vec::new();
        reference.push(
            CompressedData::new(CompressionAlgorithm::Uncompressed)
                .push(CompressedData::new(CompressionAlgorithm::Uncompressed)
                      .push(one.into())
                      .push(two.into())
                      .into())
                .push(CompressedData::new(CompressionAlgorithm::Uncompressed)
                      .push(three.into())
                      .push(four.into())
                      .into())
                .into());

        let mut o = vec![];
        {
            let m = Message::new(&mut o);
            let c0 = Compressor::new(
                m, CompressionAlgorithm::Uncompressed).unwrap();
            let c = Compressor::new(
                c0, CompressionAlgorithm::Uncompressed).unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "one").unwrap();
            let c = ls.finalize_one().unwrap().unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "two").unwrap();
            let c = ls.finalize_one().unwrap().unwrap();
            let c0 = c.finalize_one().unwrap().unwrap();
            let c = Compressor::new(
                c0, CompressionAlgorithm::Uncompressed).unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "three").unwrap();
            let c = ls.finalize_one().unwrap().unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            write!(ls, "four").unwrap();
        }

        let pile = PacketPile::from(reference);
        let pile2 = PacketPile::from_bytes(&o).unwrap();
        if pile != pile2 {
            eprintln!("REFERENCE...");
            pile.pretty_print();
            eprintln!("REPARSED...");
            pile2.pretty_print();
            panic!("Reparsed packet does not match reference packet!");
        }
    }

    #[cfg(feature = "compression-deflate")]
    #[test]
    fn stream_big() {
        let zeros = vec![0; 1024 * 1024 * 4];
        let mut o = vec![];
        {
            let m = Message::new(&mut o);
            let c = Compressor::new(m,
                                    CompressionAlgorithm::BZip2).unwrap();
            let mut ls = LiteralWriter::new(c, T, None, None).unwrap();
            // Write 64 megabytes of zeroes.
            for _ in 0 .. 16 {
                ls.write_all(&zeros).unwrap();
            }
        }
        assert!(o.len() < 1024);
    }

    #[test]
    fn signature() {
        use crypto::KeyPair;
        use std::collections::HashMap;
        use Fingerprint;

        let mut keys: HashMap<Fingerprint, Key> = HashMap::new();
        for tsk in &[
            TPK::from_bytes(::tests::key("testy-private.pgp")).unwrap(),
            TPK::from_bytes(::tests::key("testy-new-private.pgp")).unwrap(),
        ] {
            for key in tsk.keys_all().signing_capable().map(|x| x.2)
            {
                keys.insert(key.fingerprint(), key.clone());
            }
        }

        let mut o = vec![];
        {
            let mut signers = keys.iter().map(|(_, key)| {
                key.clone().into_keypair()
                    .expect("expected unencrypted secret key")
            }).collect::<Vec<KeyPair>>();

            let m = Message::new(&mut o);
            let signer = Signer::new(
                m,
                signers.iter_mut()
                    .map(|s| -> &mut dyn crypto::Signer {s})
                    .collect(),
                None)
                .unwrap();
            let mut ls = LiteralWriter::new(signer, T, None, None).unwrap();
            ls.write_all(b"Tis, tis, tis.  Tis is important.").unwrap();
            let signer = ls.finalize_one().unwrap().unwrap();
            let _ = signer.finalize_one().unwrap().unwrap();
        }

        let mut ppr = PacketParser::from_bytes(&o).unwrap();
        let mut good = 0;
        while let PacketParserResult::Some(pp) = ppr {
            if let Packet::Signature(ref sig) = pp.packet {
                let key = keys.get(&sig.issuer_fingerprint().unwrap())
                    .unwrap();
                let result = sig.verify(key).unwrap();
                assert!(result);
                good += 1;
            }

            // Get the next packet.
            ppr = pp.recurse().unwrap().1;
        }
        assert_eq!(good, 2);
    }

    #[test]
    fn encryptor() {
        let passwords: [Password; 2] = ["streng geheim".into(),
                                        "top secret".into()];
        let message = b"Hello world.";

        // Write a simple encrypted message...
        let mut o = vec![];
        {
            let m = Message::new(&mut o);
            let encryptor = Encryptor::new(
                m, &passwords.iter().collect::<Vec<&Password>>(),
                &[], EncryptionMode::ForTransport, None)
                .unwrap();
            let mut literal = LiteralWriter::new(encryptor, DataFormat::Binary,
                                                 None, None)
                .unwrap();
            literal.write_all(message).unwrap();
        }

        // ... and recover it...
        #[derive(Debug, PartialEq)]
        enum State {
            Start,
            Decrypted(Vec<(SymmetricAlgorithm, SessionKey)>),
            Deciphered,
            MDC,
            Done,
        }

        // ... with every password.
        for password in &passwords {
            let mut state = State::Start;
            let mut ppr = PacketParser::from_bytes(&o).unwrap();
            while let PacketParserResult::Some(mut pp) = ppr {
                state = match state {
                    // Look for the SKESK packet.
                    State::Start =>
                        if let Packet::SKESK(ref skesk) = pp.packet {
                            match skesk.decrypt(password) {
                                Ok((algo, key))
                                    => State::Decrypted(
                                        vec![(algo, key)]),
                                Err(e) =>
                                    panic!("Decryption failed: {}", e),
                            }
                        } else {
                            panic!("Unexpected packet: {:?}", pp.packet)
                        },

                    // Look for the SEIP packet.
                    State::Decrypted(mut keys) =>
                        match pp.packet {
                            Packet::SEIP(_) =>
                                loop {
                                    if let Some((algo, key)) = keys.pop() {
                                        let r = pp.decrypt(algo, &key);
                                        if r.is_ok() {
                                            break State::Deciphered;
                                        }
                                    } else {
                                        panic!("seip decryption failed");
                                    }
                                },
                            Packet::SKESK(ref skesk) =>
                                match skesk.decrypt(password) {
                                    Ok((algo, key)) => {
                                        keys.push((algo, key));
                                        State::Decrypted(keys)
                                    },
                                    Err(e) =>
                                        panic!("Decryption failed: {}", e),
                                },
                            _ =>
                                panic!("Unexpected packet: {:?}", pp.packet),
                        },

                    // Look for the literal data packet.
                    State::Deciphered =>
                        if let Packet::Literal(_) = pp.packet {
                            let mut body = Vec::new();
                            pp.read_to_end(&mut body).unwrap();
                            assert_eq!(&body, message);
                            State::MDC
                        } else {
                            panic!("Unexpected packet: {:?}", pp.packet)
                        },

                    // Look for the MDC packet.
                    State::MDC =>
                        if let Packet::MDC(ref mdc) = pp.packet {
                            assert_eq!(mdc.hash(), mdc.computed_hash());
                            State::Done
                        } else {
                            panic!("Unexpected packet: {:?}", pp.packet)
                        },

                    State::Done =>
                        panic!("Unexpected packet: {:?}", pp.packet),
                };

                // Next?
                ppr = pp.recurse().unwrap().1;
            }
            assert_eq!(state, State::Done);
        }
    }
}
