//! A Rust implementation of the Stateless OpenPGP Interface.
//!
//! This crate defines an interface that is the Rust equivalent of the
//! [draft 08] of the Stateless OpenPGP Command Line Interface.  Note
//! that you need an concrete implementation of this interface (such
//! as [`sequoia-sop`]) in order to use it.
//!
//!   [draft 08]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html
//!   [`sequoia-sop`]: https://docs.rs/sequoia-sop
//!
//! # Examples
//!
//! Given a reference to a [`SOP`] implementation, which is the main
//! entry point for every SOP operation, generate keys, extract certs,
//! sign, verify, encrypt, and decrypt:
//!
//! ```rust
//! # use sop::*; use std::io::Cursor;
//! # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
//! let alice_sec = sop.generate_key()?
//!     .userid("Alice Lovelace <alice@openpgp.example>")
//!     .generate()?;
//! let alice_pgp = sop.extract_cert()?
//!     .keys(&alice_sec)?;
//!
//! let bob_sec = sop.generate_key()?
//!     .userid("Bob Babbage <bob@openpgp.example>")
//!     .generate()?;
//! let bob_pgp = sop.extract_cert()?
//!     .keys(&bob_sec)?;
//!
//! let statement = b"Hello World :)";
//! let mut data = Cursor::new(&statement);
//! let (_micalg, signature) = sop.sign()?
//!     .mode(ops::SignAs::Text)
//!     .keys(&alice_sec)?
//!     .data(&mut data)?;
//!
//! let verifications = sop.verify()?
//!     .certs(&alice_pgp)?
//!     .signatures(&signature)?
//!     .data(&mut Cursor::new(&statement))?;
//! assert_eq!(verifications.len(), 1);
//!
//! let mut statement_cur = Cursor::new(&statement);
//! let (_session_key, ciphertext) = sop.encrypt()?
//!     .sign_with_keys(&alice_sec)?
//!     .with_certs(&bob_pgp)?
//!     .plaintext(&mut statement_cur)?
//!     .to_vec()?;
//!
//! let mut ciphertext_cur = Cursor::new(&ciphertext);
//! let (_, plaintext) = sop.decrypt()?
//!     .with_keys(&bob_sec)?
//!     .ciphertext(&mut ciphertext_cur)?
//!     .to_vec()?;
//! assert_eq!(&plaintext, statement);
//! # Ok(()) }
//! ```
//!
//! The above snippet is the equivalent of the following SOP command
//! line example from the SOP spec:
//!
//! ```sh
//! $ sop generate-key "Alice Lovelace <alice@openpgp.example>" > alice.sec
//! $ sop extract-cert < alice.sec > alice.pgp
//!
//! $ sop sign --as=text alice.sec < statement.txt > statement.txt.asc
//! $ sop verify announcement.txt.asc alice.pgp < announcement.txt
//!
//! $ sop encrypt --sign-with=alice.sec bob.pgp < msg.eml > encrypted.asc
//! $ sop decrypt alice.sec < ciphertext.asc > cleartext.out
//! ```

use std::{
    fmt,
    io,
};

pub mod ops;
use ops::*;
pub mod errors;
use errors::*;
pub mod plumbing;

#[cfg(any(feature = "cli", feature = "cliv"))]
pub mod cli;

/// Loads objects like certs and keys.
pub trait Load<'s, S: SOP<'s>> {
    /// Loads objects like certs and keys from stdin.
    fn from_stdin(sop: &'s S) -> Result<Self>
    where
        Self: Sized,
    {
        Self::from_reader(sop, &mut io::stdin(), Some("/dev/stdin".into()))
    }

    /// Loads objects like certs and keys from the given file.
    fn from_file<P>(sop: &'s S, path: P) -> Result<Self>
    where
        Self: Sized,
        P: AsRef<std::path::Path>,
    {
        let path = path.as_ref();
        Self::from_reader(sop, &mut std::fs::File::open(path)?,
                          Some(path.display().to_string()))
    }

    /// Loads objects like certs and keys from the given reader.
    fn from_reader(sop: &'s S, source: &mut (dyn io::Read + Send + Sync),
                   source_name: Option<String>)
                   -> Result<Self>
    where Self: Sized;

    /// Loads objects like certs and keys from the given byte slice.
    fn from_bytes(sop: &'s S, source: &[u8]) -> Result<Self>
    where
        Self: Sized,
    {
        Self::from_reader(sop, &mut io::Cursor::new(source), None)
    }

    /// Returns the source name, if any.
    fn source_name(&self) -> Option<&str>;
}

/// Saves objects like certs and keys.
pub trait Save {
    /// Writes objects like certs and keys to stdout.
    fn to_stdout(&self, armored: bool) -> Result<()> {
        self.to_writer(armored, &mut io::stdout())
    }

    /// Saves objects like certs and keys, writing them to the given writer.
    fn to_writer(&self, armored: bool, sink: &mut (dyn io::Write + Send + Sync))
                 -> Result<()>;

    /// Saves objects like certs and keys, writing them to a vector.
    fn to_vec(&self, armored: bool) -> Result<Vec<u8>> {
        let mut sink = vec![];
        self.to_writer(armored, &mut sink)?;
        Ok(sink)
    }
}

/// Main entry point to the Stateless OpenPGP Interface.
pub trait SOP<'s>: Sized {
    /// Secret keys.
    type Keys: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;

    /// Public keys.
    type Certs: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;

    /// Signatures.
    type Sigs: Load<'s, Self> + Save + plumbing::SopRef<'s, Self>;

    /// Controls debugging.
    ///
    /// If enabled, implementations may emit debugging information in
    /// any way they see fit, most commonly by printing to stderr.
    fn debug(&mut self, enable: bool) {
        let _ = enable;
    }

    /// Gets SOP version information.
    ///
    /// The default implementation returns the version of the spec
    /// that this framework supports.  This should be fine for most
    /// implementations.  However, implementations may chose to
    /// override this function to return a more nuanced response.
    fn spec_version(&'s self) -> &'static str {
        "~draft-dkg-openpgp-stateless-cli-11"
    }

    /// Completeness of the sopv subset
    ///
    /// If all the features of the sopv subset are implemented, an
    /// implementation should return Ok("1.0")
    fn sopv_version(&'s self) -> Result<&'static str> {
        Err(Error::UnsupportedOption)
    }

    /// Gets version information.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor;
    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
    /// // Prints the name of the SOP implementation.
    /// println!("{}", sop.version()?.frontend()?);
    ///
    /// // Prints the name of the underlying OpenPGP implementation.
    /// println!("{}", sop.version()?.backend()?);
    ///
    /// // Prints extended version information.
    /// println!("{}", sop.version()?.extended()?);
    /// # Ok(()) }
    /// ```
    fn version(&self) -> Result<Box<dyn Version>>;

    /// Generates a Secret Key.
    ///
    /// Customize the operation using the builder [`GenerateKey`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor;
    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
    /// let alice_sec = sop.generate_key()?
    ///     .userid("Alice Lovelace <alice@openpgp.example>")
    ///     .generate()?;
    /// # Ok(()) }
    /// ```
    fn generate_key(&'s self)
                    -> Result<Box<dyn GenerateKey<Self, Self::Keys> + 's>>;

    /// Updates a key's password.
    ///
    /// Customize the operation using the builder [`ChangeKeyPassword`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    ///
    /// let alice_updated_secret = sop.change_key_password()?
    ///     .old_key_password(Password::new_unchecked(b"hunter2".to_vec()))?
    ///     .new_key_password(Password::new(b"jaeger2".to_vec())?)?
    ///     .keys(&alice_secret)?;
    /// # Ok(()) }
    /// ```
    fn change_key_password(&'s self)
                           -> Result<Box<dyn ChangeKeyPassword<Self, Self::Keys> + 's>>;

    /// Creates a Revocation Certificate.
    ///
    /// Customize the operation using the builder [`RevokeKey`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    ///
    /// let alice_revoked = sop.revoke_key()?
    ///     .with_key_password(Password::new_unchecked(b"hunter2".to_vec()))?
    ///     .keys(&alice_secret)?;
    /// # Ok(()) }
    /// ```
    fn revoke_key(&'s self)
                  -> Result<Box<dyn RevokeKey<Self, Self::Certs, Self::Keys> + 's>>;

    /// Extracts a Certificate from a Secret Key.
    ///
    /// Customize the operation using the builder [`ExtractCert`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    ///
    /// let alice_public = sop.extract_cert()?
    ///     .keys(&alice_secret)?;
    /// # Ok(()) }
    /// ```
    fn extract_cert(&'s self) -> Result<Box<
            dyn ExtractCert<Self, Self::Certs, Self::Keys> + 's>>;

    /// Keeps a Secret Key Up-To-Date.
    ///
    /// This update will "fix" everything that the implementation
    /// knows how to fix to bring each Transferable Secret Key up to
    /// reasonable modern practice. Each Transferable Secret Key
    /// output must be capable of signing, and (unless --signing-only
    /// is provided) capable of decryption. The primary key of each
    /// Transferable Secret Key will not be changed in any way that
    /// affects its fingerprint.
    ///
    /// One important aspect of sop update-key is how it handles
    /// advertisement of support for various OpenPGP capabilities
    /// (algorithms, mechanisms, etc). All capabilities that the
    /// implementation knows it does not support, or knows to be weak
    /// and/or deprecated MUST be removed from the output Transferable
    /// Secret Keys. This includes unknown/deprecated flags in the
    /// Features subpacket, and any unknown/deprecated algorithm IDs
    /// in algorithm preferences subpackets. For example, an
    /// implementation compliant with [RFC9580] will never emit a
    /// Transferable Secret Key with a Preferred Hash Preferences
    /// subpacket that explicitly indicates support for MD5,
    /// RIPEMD160, or SHA1.
    fn update_key(&'s self) -> Result<Box<
            dyn UpdateKey<Self, Self::Certs, Self::Keys> + 's>>;

    /// Merge OpenPGP Certificates.
    ///
    /// This can be used, for example, to absorb a third-party
    /// certification into a certificate, or to update a certificate's
    /// feature advertisements without losing local annotations.
    fn merge_certs(&'s self)
                   -> Result<Box<dyn MergeCerts<Self, Self::Certs> + 's>>;

    /// Certify OpenPGP Certificate User IDs.
    ///
    /// With each Transferable Secret Key provided, add a third-party
    /// certification to the provided certificates, and
    /// emit the updated OpenPGP certificates.
    fn certify_userid(&'s self) -> Result<Box<
            dyn CertifyUserID<Self, Self::Certs, Self::Keys> + 's>>;

    /// Validate a User ID in an OpenPGP Certificate.
    ///
    /// Given a set of authority OpenPGP certificates, succeed if and
    /// only if all provided OpenPGP certificates are correctly bound
    /// by at least one valid signature from one authority to the User
    /// ID or email address in question.
    fn validate_userid(&'s self) -> Result<Box<
            dyn ValidateUserID<Self, Self::Certs> + 's>>;

    /// Creates Detached Signatures.
    ///
    /// Customize the operation using the builder [`Sign`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    ///
    /// let (_micalg, sig) = sop.sign()?
    ///     .keys(&alice_secret)?
    ///     .data(&mut Cursor::new(&b"Hello World :)"))?;
    /// # Ok(()) }
    /// ```
    fn sign(&'s self)
            -> Result<Box<dyn Sign<Self, Self::Keys, Self::Sigs> + 's>>;

    /// Verifies Detached Signatures.
    ///
    /// Customize the operation using the builder [`Verify`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys, Sigs>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys, Sigs = Sigs>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// #     Sigs: Load<'s, S> + Save,
    /// # {
    /// let alice_public =
    ///     Certs::from_file(sop, "alice.public")?;
    /// let sig =
    ///     Sigs::from_file(sop, "data.asc")?;
    ///
    /// let verifications = sop.verify()?
    ///     .certs(&alice_public)?
    ///     .signatures(&sig)?
    ///     .data(&mut Cursor::new(&b"Hello World :)"))?;
    /// let valid_signatures = ! verifications.is_empty();
    /// # Ok(()) }
    /// ```
    fn verify(&'s self)
              -> Result<Box<dyn Verify<Self, Self::Certs, Self::Sigs> + 's>>;

    /// Encrypts a Message.
    ///
    /// Customize the operation using the builder [`Encrypt`].
    ///
    /// # Examples
    ///
    /// Encrypts a message for Bob, and signs it using Alice's key.
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    /// let bob_public =
    ///     Certs::from_file(sop, "bob.public")?;
    ///
    /// let (_session_key, ciphertext) = sop.encrypt()?
    ///     .sign_with_keys(&alice_secret)?
    ///     .with_certs(&bob_public)?
    ///     .plaintext(&mut Cursor::new(&b"Hello World :)"))?
    ///     .to_vec()?;
    /// # Ok(()) }
    /// ```
    fn encrypt(&'s self)
               -> Result<Box<dyn Encrypt<Self, Self::Certs, Self::Keys> + 's>>;

    /// Decrypts a Message.
    ///
    /// Customize the operation using the builder [`Decrypt`].
    ///
    /// # Examples
    ///
    /// Decrypts a message encrypted for Bob, and verifies Alice's
    /// signature on it.
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_public =
    ///     Certs::from_file(sop, "alice.public")?;
    /// let bob_secret =
    ///     Keys::from_file(sop, "bob.secret")?;
    ///
    /// let ((_session_key, verifications), plaintext) = sop.decrypt()?
    ///     .verify_with_certs(&alice_public)?
    ///     .with_keys(&bob_secret)?
    ///     .ciphertext(&mut File::open("ciphertext.pgp")?)?
    ///     .to_vec()?;
    /// let valid_signatures = ! verifications.is_empty();
    /// # Ok(()) }
    /// ```
    fn decrypt(&'s self)
               -> Result<Box<dyn Decrypt<Self, Self::Certs, Self::Keys> + 's>>;

    /// Converts binary OpenPGP data to ASCII.
    ///
    /// By default, SOP operations emit ASCII-Armored data.  But,
    /// occasionally it can be useful to explicitly armor data.
    ///
    /// Customize the operation using the builder [`Armor`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
    /// let (_, alice_secret_asc) = sop.armor()?
    ///     .data(&mut File::open("alice.secret.bin")?)?
    ///     .to_vec()?;
    /// assert!(alice_secret_asc.starts_with(b"-----BEGIN PGP PRIVATE KEY BLOCK-----"));
    /// # Ok(()) }
    /// ```
    fn armor(&'s self) -> Result<Box<dyn Armor + 's>>;

    /// Converts ASCII OpenPGP data to binary.
    ///
    /// By default, SOP operations emit ASCII-Armored data, but this
    /// behavior can be changed at export time.  Nevertheless,
    /// occasionally it can be useful to explicitly dearmor data.
    ///
    /// Customize the operation using the builder [`Dearmor`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
    /// let (_, alice_secret_bin) = sop.dearmor()?
    ///     .data(&mut File::open("alice.secret.asc")?)?
    ///     .to_vec()?;
    /// assert!(! alice_secret_bin.starts_with(b"-----BEGIN PGP PRIVATE KEY BLOCK-----"));
    /// # Ok(()) }
    /// ```
    fn dearmor(&'s self) -> Result<Box<dyn Dearmor + 's>>;

    /// Splits Signatures from an Inline-Signed Message.
    ///
    /// Note: The signatures are not verified, this merely transforms
    /// an inline-signed message into a detached signature, which in
    /// turn can be verified using [`SOP::verify`].
    ///
    /// Customize the operation using the builder [`InlineDetach`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S: SOP<'s> + 's>(sop: &'s S) -> Result<()> {
    /// let (signatures, data) = sop.inline_detach()?
    ///     .message(&mut File::open("inline-signed.pgp")?)?
    ///     .to_vec()?;
    /// # Ok(()) }
    /// ```
    fn inline_detach(&'s self)
                     -> Result<Box<dyn InlineDetach<Self::Sigs> + 's>>;

    /// Verifies an Inline-Signed Message.
    ///
    /// Customize the operation using the builder [`InlineVerify`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_public =
    ///     Certs::from_file(sop, "alice.public")?;
    ///
    /// let (verifications, data) = sop.inline_verify()?
    ///     .certs(&alice_public)?
    ///     .message(&mut File::open("inline-signed.pgp")?)?
    ///     .to_vec()?;
    /// let valid_signatures = ! verifications.is_empty();
    /// # Ok(()) }
    /// ```
    fn inline_verify(&'s self)
                     -> Result<Box<dyn InlineVerify<Self, Self::Certs> + 's>>;

    /// Creates an Inline-Signed Message.
    ///
    /// Customize the operation using the builder [`InlineSign`].
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use sop::*; use std::io::Cursor; use std::fs::File;
    /// # fn sop_examples<'s, S, Certs, Keys>(sop: &'s S) -> Result<()>
    /// # where
    /// #     S: SOP<'s, Certs = Certs, Keys = Keys>,
    /// #     Certs: Load<'s, S> + Save,
    /// #     Keys: Load<'s, S> + Save,
    /// # {
    /// let alice_secret =
    ///     Keys::from_file(sop, "alice.secret")?;
    ///
    /// let (inline_signed_asc) = sop.inline_sign()?
    ///     .keys(&alice_secret)?
    ///     .data(&mut Cursor::new(&b"Hello World :)"))?
    ///     .to_vec()?;
    /// # Ok(()) }
    /// ```
    fn inline_sign(&'s self)
                   -> Result<Box<dyn InlineSign<Self, Self::Keys> + 's>>;
}

/// A password.
///
/// See [Passwords are Human-Readable] in the SOP spec.
///
///   [Passwords are Human-Readable]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-passwords-are-human-readabl
pub struct Password(Box<[u8]>);

impl Password {
    /// Returns a `Password` that is guaranteed to be human-readable.
    ///
    /// Use this function when you get a password from the user to
    /// generate an artifact with (see [Generating Material with
    /// Human-Readable Passwords]).
    ///
    /// [Generating Material with Human-Readable Passwords]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-generating-material-with-hu
    pub fn new(password: Vec<u8>) -> Result<Password> {
        // Securely erase the password.
        fn securely_erase(mut p: Vec<u8>) {
            unsafe {
                memsec::memzero(p.as_mut_ptr(), p.len());
            }
        }

        let mut s = String::from_utf8(password)
            .map_err(|e| {
                securely_erase(e.into_bytes());
                Error::PasswordNotHumanReadable
            })?;

        // Check for leading whitespace.
        if s.trim_start().len() != s.len() {
            securely_erase(s.into_bytes());
            return Err(Error::PasswordNotHumanReadable);
        }

        // Trim trailing whitespace.
        s.truncate(s.trim_end().len());

        // Check for odd whitespace.
        if s.chars().any(|c| c.is_whitespace() && c != ' ') {
            securely_erase(s.into_bytes());
            return Err(Error::PasswordNotHumanReadable);
        }

        // XXX: Check that the password is in Unicode Normal Form C,
        // but I don't think that is possible with Rust's stdlib.

        Ok(Password(s.into_bytes().into()))
    }

    /// Returns a `Password` without further checking.
    ///
    /// Use this function when you get a password from the user that
    /// is used when consuming an artifact (see [Consuming
    /// Password-protected Material]).
    ///
    /// [Consuming Password-protected Material]: https://www.ietf.org/archive/id/draft-dkg-openpgp-stateless-cli-08.html#name-consuming-password-protecte
    pub fn new_unchecked(password: Vec<u8>) -> Password {
        Password(password.into())
    }
}

impl plumbing::PasswordsAreHumanReadable for Password {
    fn normalized(&self) -> &[u8] {
        // First, let's hope it is UTF-8.
        if let Ok(p) = std::str::from_utf8(&self.0) {
            p.trim_end().as_bytes()
        } else {
            // As a best effort for now, drop ASCII-whitespace from
            // the end.
            let mut p = &self.0[..];
            while ! p.is_empty() && p[p.len() - 1].is_ascii_whitespace() {
                p = &p[..p.len() - 1];
            }
            p
        }
    }

    fn variants(&self) -> Box<dyn Iterator<Item = &[u8]> + '_> {
        Box::new(std::iter::once(self.normalized())
            .filter_map(move |normalized| {
                if normalized.len() < self.0.len() {
                    Some(normalized)
                } else {
                    None
                }
            })
            .chain(std::iter::once(&self.0[..])))
    }
}

impl Drop for Password {
    fn drop(&mut self) {
        unsafe {
            memsec::memzero(self.0.as_mut_ptr(), self.0.len());
        }
    }
}

/// A session key.
pub struct SessionKey {
    algorithm: u8,
    key: Box<[u8]>,
}

impl SessionKey {
    /// Creates a new session key object.
    pub fn new<A, K>(algorithm: A, key: K) -> Result<Self>
    where A: Into<u8>,
          K: AsRef<[u8]>,
    {
        // XXX: Maybe sanity check key lengths.
        Ok(SessionKey {
            algorithm: algorithm.into(),
            key: key.as_ref().to_vec().into(),
        })
    }

    /// Returns the symmetric algorithm octet.
    pub fn algorithm(&self) -> u8 {
        self.algorithm
    }

    /// Returns the session key.
    pub fn key(&self) -> &[u8] {
        &self.key
    }
}

impl fmt::Display for SessionKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:", self.algorithm)?;
        for b in &self.key[..] {
            write!(f, "{:02X}", b)?
        }
        Ok(())
    }
}

impl Drop for SessionKey {
    fn drop(&mut self) {
        unsafe {
            memsec::memzero(self.key.as_mut_ptr(), self.key.len());
        }
    }
}

impl std::str::FromStr for SessionKey {
    type Err = ParseError;
    fn from_str(sk: &str) -> ParseResult<Self> {
        // The SOP format is:
        //
        //   <decimal-cipher-octet> ":" <hex-session-key>
        //
        // We most likely will change the first field, so we split
        // from the end of the string using `rsplit`, which puts the
        // last segment first.  This is rather unexpected.  Reverse
        // it.
        let fields = sk.rsplit(':').rev().collect::<Vec<_>>();

        if fields.len() != 2 {
            return Err(ParseError(format!(
                "Expected two colon-separated fields, got {:?}",
                fields)));
        }

        let algo: u8 = fields[0].parse().map_err(
            |e| ParseError(format!("Failed to parse algorithm: {}", e)))?;
        let sk = from_hex(&fields[1], true)?;
        Self::new(algo, sk).map_err(
            |e| ParseError(format!("Bad session key: {}", e)))
    }
}

/// A helpful function for converting a hexadecimal string to binary.
/// This function skips whitespace if `pretty` is set.
fn from_hex(hex: &str, pretty: bool) -> ParseResult<Vec<u8>> {
    const BAD: u8 = 255u8;
    const X: u8 = 'x' as u8;

    let mut nibbles = hex.chars().filter_map(|x| {
        match x {
            '0' => Some(0u8),
            '1' => Some(1u8),
            '2' => Some(2u8),
            '3' => Some(3u8),
            '4' => Some(4u8),
            '5' => Some(5u8),
            '6' => Some(6u8),
            '7' => Some(7u8),
            '8' => Some(8u8),
            '9' => Some(9u8),
            'a' | 'A' => Some(10u8),
            'b' | 'B' => Some(11u8),
            'c' | 'C' => Some(12u8),
            'd' | 'D' => Some(13u8),
            'e' | 'E' => Some(14u8),
            'f' | 'F' => Some(15u8),
            'x' | 'X' if pretty => Some(X),
            _ if pretty && x.is_whitespace() => None,
            _ => Some(BAD),
        }
    }).collect::<Vec<u8>>();

    if pretty && nibbles.len() >= 2 && nibbles[0] == 0 && nibbles[1] == X {
        // Drop '0x' prefix.
        nibbles.remove(0);
        nibbles.remove(0);
    }

    if nibbles.iter().any(|&b| b == BAD || b == X) {
        // Not a hex character.
        return Err(ParseError("Invalid characters".into()));
    }

    // We need an even number of nibbles.
    if nibbles.len() % 2 != 0 {
        return Err(ParseError("Odd number of nibbles".into()));
    }

    let bytes = nibbles.chunks(2).map(|nibbles| {
        (nibbles[0] << 4) | nibbles[1]
    }).collect::<Vec<u8>>();

    Ok(bytes)
}

/// Result specialization.
pub type Result<T> = std::result::Result<T, Error>;

/// Convenience alias.
type ParseResult<T> = std::result::Result<T, ParseError>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn session_key_roundtrip() -> Result<()> {
        for algo in &[9, 13] {
            let sk = SessionKey::new(
                *algo,
                &[0xE1, 0x48, 0x97, 0x81, 0xAA, 0x22, 0xE1, 0xBF,
                  0x6E, 0x3E, 0x61, 0x74, 0x8C, 0x8D, 0x3F, 0x35,
                  0x50, 0x7C, 0x80, 0x9E, 0x95, 0x64, 0x86, 0x87,
                  0xC7, 0xE4, 0xB9, 0xAF, 0x86, 0x17, 0xD3, 0xAE])?;
            let sk_s = sk.to_string();
            let sk_p: SessionKey = sk_s.parse().unwrap();
            assert_eq!(sk.algorithm(), sk_p.algorithm());
            assert_eq!(sk.key(), sk_p.key());
        }
        Ok(())
    }

    #[test]
    fn sign_as_roundtrip() -> Result<()> {
        use SignAs::*;
        for a in &[Text, Binary] {
            let s = a.to_string();
            let b: SignAs = s.parse().unwrap();
            assert_eq!(a, &b);
        }
        Ok(())
    }

    #[test]
    fn encrypt_as_roundtrip() -> Result<()> {
        use EncryptAs::*;
        for a in &[Text, Binary] {
            let s = a.to_string();
            let b: EncryptAs = s.parse().unwrap();
            assert_eq!(a, &b);
        }
        Ok(())
    }

    #[test]
    fn armor_label_roundtrip() -> Result<()> {
        use ArmorLabel::*;
        for a in &[Auto, Sig, Key, Cert, Message] {
            let s = a.to_string();
            let b: ArmorLabel = s.parse().unwrap();
            assert_eq!(a, &b);
        }
        Ok(())
    }
}
