use std::time;
use std::marker::PhantomData;

use crate::packet;
use crate::packet::{
    Key,
    key::Key4,
    key::Key6,
    key::UnspecifiedRole,
    key::SecretKey as KeySecretKey,
    key::SecretParts as KeySecretParts,
};
use crate::Profile;
use crate::Result;
use crate::packet::Signature;
use crate::packet::signature::{
    self,
    SignatureBuilder,
    subpacket::SubpacketTag,
};
use crate::cert::prelude::*;
use crate::Error;
use crate::crypto::{Password, Signer};
use crate::types::{
    Features,
    HashAlgorithm,
    KeyFlags,
    SignatureType,
    SymmetricAlgorithm,
    RevocationKey,
};

mod key;
pub use key::{
    KeyBuilder,
    SubkeyBuilder,
};

/// Groups symmetric and asymmetric algorithms.
///
/// This is used to select a suite of ciphers.
///
/// # Examples
///
/// ```
/// use sequoia_openpgp as openpgp;
/// use openpgp::cert::prelude::*;
/// use openpgp::types::PublicKeyAlgorithm;
///
/// # fn main() -> openpgp::Result<()> {
/// let (ecc, _) =
///     CertBuilder::general_purpose(Some("alice@example.org"))
///         .set_cipher_suite(CipherSuite::Cv25519)
///         .generate()?;
/// assert_eq!(ecc.primary_key().key().pk_algo(), PublicKeyAlgorithm::EdDSA);
///
/// let (rsa, _) =
///     CertBuilder::general_purpose(Some("alice@example.org"))
///         .set_cipher_suite(CipherSuite::RSA4k)
///         .generate()?;
/// assert_eq!(rsa.primary_key().key().pk_algo(), PublicKeyAlgorithm::RSAEncryptSign);
/// # Ok(())
/// # }
/// ```
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum CipherSuite {
    /// EdDSA and ECDH over Curve25519 with SHA512 and AES256
    Cv25519,
    /// EdDSA and ECDH over Curve448 with SHA512 and AES256
    Cv448,
    /// 3072 bit RSA with SHA512 and AES256
    RSA3k,
    /// EdDSA and ECDH over NIST P-256 with SHA256 and AES256
    P256,
    /// EdDSA and ECDH over NIST P-384 with SHA384 and AES256
    P384,
    /// EdDSA and ECDH over NIST P-521 with SHA512 and AES256
    P521,
    /// 2048 bit RSA with SHA512 and AES256
    RSA2k,
    /// 4096 bit RSA with SHA512 and AES256
    RSA4k,

    // If you add a variant here, be sure to update
    // CipherSuite::variants below.
}
assert_send_and_sync!(CipherSuite);

impl Default for CipherSuite {
    fn default() -> Self {
        CipherSuite::Cv25519
    }
}

impl CipherSuite {
    /// Returns an iterator over `CipherSuite`'s  variants.
    pub fn variants() -> impl Iterator<Item=CipherSuite> {
        use CipherSuite::*;

        [ Cv25519, Cv448, RSA3k, P256, P384, P521, RSA2k, RSA4k ]
            .into_iter()
    }

    /// Returns whether the currently selected cryptographic backend
    /// supports the encryption and signing algorithms that the cipher
    /// suite selects.
    pub fn is_supported(&self) -> Result<()> {
        use crate::types::{Curve, PublicKeyAlgorithm};
        use CipherSuite::*;

        macro_rules! check_pk {
            ($pk: expr) => {
                if ! $pk.is_supported() {
                    return Err(Error::UnsupportedPublicKeyAlgorithm($pk)
                               .into());
                }
            }
        }

        macro_rules! check_curve {
            ($curve: expr) => {
                if ! $curve.is_supported() {
                    return Err(Error::UnsupportedEllipticCurve($curve)
                               .into());
                }
            }
        }

        match self {
            Cv25519 => {
                check_pk!(PublicKeyAlgorithm::EdDSA);
                check_curve!(Curve::Ed25519);
                check_pk!(PublicKeyAlgorithm::ECDH);
                check_curve!(Curve::Cv25519);
            },
            Cv448 => {
                check_pk!(PublicKeyAlgorithm::X448);
                check_pk!(PublicKeyAlgorithm::Ed448);
            },
            RSA2k | RSA3k | RSA4k => {
                check_pk!(PublicKeyAlgorithm::RSAEncryptSign);
            },
            P256 => {
                check_pk!(PublicKeyAlgorithm::ECDSA);
                check_curve!(Curve::NistP256);
                check_pk!(PublicKeyAlgorithm::ECDH);
            },
            P384 => {
                check_pk!(PublicKeyAlgorithm::ECDSA);
                check_curve!(Curve::NistP384);
                check_pk!(PublicKeyAlgorithm::ECDH);
            },
            P521 => {
                check_pk!(PublicKeyAlgorithm::ECDSA);
                check_curve!(Curve::NistP521);
                check_pk!(PublicKeyAlgorithm::ECDH);
            },
        }
        Ok(())
    }

    fn generate_key<K>(self, flags: K, profile: Profile)
        -> Result<Key<KeySecretParts, UnspecifiedRole>>
        where K: AsRef<KeyFlags>,
    {
        match profile {
            Profile::RFC9580 => Ok(self.generate_v6_key(flags)?.into()),
            Profile::RFC4880 => Ok(self.generate_v4_key(flags)?.into()),
        }
    }

    fn generate_v4_key<K>(self, flags: K)
        -> Result<Key4<KeySecretParts, UnspecifiedRole>>
        where K: AsRef<KeyFlags>,
    {
        use crate::types::Curve;

        match self {
            CipherSuite::RSA2k =>
                Key4::generate_rsa(2048),
            CipherSuite::RSA3k =>
                Key4::generate_rsa(3072),
            CipherSuite::RSA4k =>
                Key4::generate_rsa(4096),
            CipherSuite::Cv448 => {
                let flags = flags.as_ref();
                let sign = flags.for_certification() || flags.for_signing()
                    || flags.for_authentication();
                let encrypt = flags.for_transport_encryption()
                    || flags.for_storage_encryption();
                match (sign, encrypt) {
                    (true, false) => Key4::generate_ed448(),
                    (false, true) => Key4::generate_x448(),
                    (true, true) =>
                        Err(Error::InvalidOperation(
                            "Can't use key for encryption and signing".into())
                            .into()),
                    (false, false) =>
                        Err(Error::InvalidOperation(
                            "No key flags set".into())
                            .into()),
                }
            }
            CipherSuite::Cv25519 | CipherSuite::P256 |
            CipherSuite::P384 | CipherSuite::P521 => {
                let flags = flags.as_ref();
                let sign = flags.for_certification() || flags.for_signing()
                    || flags.for_authentication();
                let encrypt = flags.for_transport_encryption()
                    || flags.for_storage_encryption();
                let curve = match self {
                    CipherSuite::Cv25519 if sign => Curve::Ed25519,
                    CipherSuite::Cv25519 if encrypt => Curve::Cv25519,
                    CipherSuite::Cv25519 => {
                        return Err(Error::InvalidOperation(
                            "No key flags set".into())
                            .into());
                    }
                    CipherSuite::P256 => Curve::NistP256,
                    CipherSuite::P384 => Curve::NistP384,
                    CipherSuite::P521 => Curve::NistP521,
                    _ => unreachable!(),
                };

                match (sign, encrypt) {
                    (true, false) => Key4::generate_ecc(true, curve),
                    (false, true) => Key4::generate_ecc(false, curve),
                    (true, true) =>
                        Err(Error::InvalidOperation(
                            "Can't use key for encryption and signing".into())
                            .into()),
                    (false, false) =>
                        Err(Error::InvalidOperation(
                            "No key flags set".into())
                            .into()),
                }
            },
        }
    }

    fn generate_v6_key<K>(self, flags: K)
        -> Result<Key6<KeySecretParts, UnspecifiedRole>>
        where K: AsRef<KeyFlags>,
    {
        use crate::types::Curve;

        let flags = flags.as_ref();
        let sign = flags.for_certification() || flags.for_signing()
            || flags.for_authentication();
        let encrypt = flags.for_transport_encryption()
            || flags.for_storage_encryption();

        match self {
            CipherSuite::Cv25519 => match (sign, encrypt) {
                (true, false) => Key6::generate_ed25519(),
                (false, true) => Key6::generate_x25519(),
                (true, true) =>
                    Err(Error::InvalidOperation(
                        "Can't use key for encryption and signing".into())
                        .into()),
                (false, false) =>
                    Err(Error::InvalidOperation(
                        "No key flags set".into())
                        .into()),
            },
            CipherSuite::RSA2k =>
                Key6::generate_rsa(2048),
            CipherSuite::RSA3k =>
                Key6::generate_rsa(3072),
            CipherSuite::RSA4k =>
                Key6::generate_rsa(4096),
            CipherSuite::Cv448 => match (sign, encrypt) {
                (true, false) => Key6::generate_ed448(),
                (false, true) => Key6::generate_x448(),
                (true, true) =>
                    Err(Error::InvalidOperation(
                        "Can't use key for encryption and signing".into())
                        .into()),
                (false, false) =>
                    Err(Error::InvalidOperation(
                        "No key flags set".into())
                        .into()),
            },
            CipherSuite::P256 | CipherSuite::P384 | CipherSuite::P521 => {
                let curve = match self {
                    CipherSuite::Cv25519 if sign => Curve::Ed25519,
                    CipherSuite::Cv25519 if encrypt => Curve::Cv25519,
                    CipherSuite::Cv25519 => {
                        return Err(Error::InvalidOperation(
                            "No key flags set".into())
                            .into());
                    }
                    CipherSuite::P256 => Curve::NistP256,
                    CipherSuite::P384 => Curve::NistP384,
                    CipherSuite::P521 => Curve::NistP521,
                    _ => unreachable!(),
                };

                match (sign, encrypt) {
                    (true, false) => Key6::generate_ecc(true, curve),
                    (false, true) => Key6::generate_ecc(false, curve),
                    (true, true) =>
                        Err(Error::InvalidOperation(
                            "Can't use key for encryption and signing".into())
                            .into()),
                    (false, false) =>
                        Err(Error::InvalidOperation(
                            "No key flags set".into())
                            .into()),
                }
            },
        }
    }
}

#[derive(Clone, Debug)]
pub struct KeyBlueprint {
    flags: KeyFlags,
    validity: Option<time::Duration>,
    // If not None, uses the specified ciphersuite.  Otherwise, uses
    // CertBuilder::ciphersuite.
    ciphersuite: Option<CipherSuite>,
}
assert_send_and_sync!(KeyBlueprint);

/// Simplifies the generation of OpenPGP certificates.
///
/// A builder to generate complex certificate hierarchies with multiple
/// [`UserID`s], [`UserAttribute`s], and [`Key`s].
///
/// This builder does not aim to be as flexible as creating
/// certificates manually, but it should be sufficiently powerful to
/// cover most use cases.
///
/// [`UserID`s]: crate::packet::UserID
/// [`UserAttribute`s]: crate::packet::user_attribute::UserAttribute
/// [`Key`s]: crate::packet::Key
///
/// # Security considerations
///
/// ## Expiration
///
/// There are two ways to invalidate cryptographic key material:
/// revocation and freshness.  Both variants come with their own
/// challenges.  Revocations rely on a robust channel to update
/// certificates (and attackers may interfere with that).
///
/// On the other hand, freshness involves creating key material that
/// expires after a certain time, then periodically extending the
/// expiration time.  Again, consumers need a way to update
/// certificates, but should that fail (maybe because it was
/// interfered with), the consumer errs on the side of no longer
/// trusting that key material.
///
/// Because of the way metadata is added to OpenPGP certificates,
/// attackers who control the certificate lookup and update mechanism
/// may strip components like signatures from the certificate.  This
/// has implications for the robustness of relying on freshness.
///
/// If you first create a certificate that does not expire, and then
/// change your mind and set an expiration time, an attacker can
/// simply strip off that update, yielding the original certificate
/// that does not expire.
///
/// Hence, to ensure robust key expiration, you must set an expiration
/// with [`CertBuilder::set_validity_period`] when you create the
/// certificate.
///
/// By default, the `CertBuilder` creates certificates that do not
/// expire, because the expiration time is a policy decision and
/// depends on the use case.  For general purpose certificates,
/// [`CertBuilder::general_purpose`] sets the validity period to
/// roughly three years.
///
/// # Examples
///
/// Generate a general-purpose certificate with one User ID:
///
/// ```
/// use sequoia_openpgp as openpgp;
/// use openpgp::cert::prelude::*;
///
/// # fn main() -> openpgp::Result<()> {
/// let (cert, rev) =
///     CertBuilder::general_purpose(Some("alice@example.org"))
///         .generate()?;
/// # Ok(())
/// # }
/// ```
pub struct CertBuilder<'a> {
    creation_time: Option<std::time::SystemTime>,
    ciphersuite: CipherSuite,
    profile: Profile,

    /// Advertised features.
    features: Features,
    primary: KeyBlueprint,
    subkeys: Vec<(Option<SignatureBuilder>, KeyBlueprint)>,
    userids: Vec<(Option<SignatureBuilder>, packet::UserID)>,
    user_attributes: Vec<(Option<SignatureBuilder>, packet::UserAttribute)>,
    password: Option<Password>,
    revocation_keys: Option<Vec<RevocationKey>>,
    exportable: bool,
    phantom: PhantomData<&'a ()>,
}
assert_send_and_sync!(CertBuilder<'_>);

impl CertBuilder<'_> {
    /// Returns a new `CertBuilder`.
    ///
    /// The returned builder is configured to generate a minimal
    /// OpenPGP certificate, a certificate with just a
    /// certification-capable primary key.  You'll typically want to
    /// add at least one User ID (using
    /// [`CertBuilder::add_userid`]). and some subkeys (using
    /// [`CertBuilder::add_signing_subkey`],
    /// [`CertBuilder::add_transport_encryption_subkey`], etc.).
    ///
    /// By default, the generated certificate does not expire.  It is
    /// recommended to set a suitable validity period using
    /// [`CertBuilder::set_validity_period`].  See [this
    /// section](CertBuilder#expiration) of the type's documentation
    /// for security considerations of key expiration.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_userid("Alice Lovelace <alice@lovelace.name>")
    ///         .add_signing_subkey()
    ///         .add_transport_encryption_subkey()
    ///         .add_storage_encryption_subkey()
    ///         .generate()?;
    /// # assert_eq!(cert.keys().count(), 1 + 3);
    /// # assert_eq!(cert.userids().count(), 1);
    /// # assert_eq!(cert.user_attributes().count(), 0);
    /// # Ok(())
    /// # }
    /// ```
    pub fn new() -> Self {
        CertBuilder {
            creation_time: None,
            ciphersuite: CipherSuite::default(),
            profile: Default::default(),
            features: Features::sequoia(),
            primary: KeyBlueprint{
                flags: KeyFlags::empty().set_certification(),
                validity: None,
                ciphersuite: None,
            },
            subkeys: vec![],
            userids: vec![],
            user_attributes: vec![],
            password: None,
            revocation_keys: None,
            exportable: true,
            phantom: PhantomData,
        }
    }

    /// Generates a general-purpose certificate.
    ///
    /// The returned builder is set to generate a certificate with a
    /// certification-capable primary key, a signing-capable subkey,
    /// and an encryption-capable subkey.  The encryption subkey is
    /// marked as being appropriate for both data in transit and data
    /// at rest.
    ///
    /// The certificate and all subkeys are valid for approximately
    /// three years.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(["Alice Lovelace", "<alice@example.org>"])
    ///         .generate()?;
    /// # assert_eq!(cert.keys().count(), 3);
    /// # assert_eq!(cert.userids().count(), 2);
    ///
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(["Alice Lovelace <alice@example.org>"])
    ///         .generate()?;
    /// # assert_eq!(cert.keys().count(), 3);
    /// # assert_eq!(cert.userids().count(), 1);
    /// # Ok(())
    /// # }
    /// ```
    pub fn general_purpose<U>(userids: U) -> Self
    where
        U: IntoIterator,
        U::Item: Into<packet::UserID>,
    {
        let mut builder = Self::new()
            .set_primary_key_flags(KeyFlags::empty().set_certification())
            .set_validity_period(
                time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0))
            .add_signing_subkey()
            .add_subkey(KeyFlags::empty()
                        .set_transport_encryption()
                        .set_storage_encryption(), None, None);

        for u in userids {
            builder = builder.add_userid(u.into());
        }

        builder
    }

    /// Sets the creation time.
    ///
    /// If `creation_time` is not `None`, this causes the
    /// `CertBuilder` to use that time when [`CertBuilder::generate`]
    /// is called.  If it is `None`, the default, then the current
    /// time minus 60 seconds is used as creation time.  Backdating
    /// the certificate by a minute has the advantage that the
    /// certificate can immediately be customized:
    ///
    /// In order to reliably override a binding signature, the
    /// overriding binding signature must be newer than the existing
    /// signature.  If, however, the existing signature is created
    /// `now`, any newer signature must have a future creation time,
    /// and is considered invalid by Sequoia.  To avoid this, we
    /// backdate certificate creation times (and hence binding
    /// signature creation times), so that there is "space" between
    /// the creation time and now for signature updates.
    ///
    /// Warning: this function takes a [`SystemTime`].  A `SystemTime`
    /// has a higher resolution, and a larger range than an OpenPGP
    /// [`Timestamp`].  Assuming the `creation_time` is in range, it
    /// will automatically be truncated to the nearest time that is
    /// representable by a `Timestamp`.  If it is not in range,
    /// [`generate`] will return an error.
    ///
    /// [`CertBuilder::generate`]: CertBuilder::generate()
    /// [`SystemTime`]: std::time::SystemTime
    /// [`Timestamp`]: crate::types::Timestamp
    /// [`generate`]: CertBuilder::generate()
    ///
    /// # Examples
    ///
    /// Generate a backdated certificate:
    ///
    /// ```
    /// use std::time::{SystemTime, Duration};
    /// use std::convert::TryFrom;
    ///
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::types::Timestamp;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let t = SystemTime::now() - Duration::from_secs(365 * 24 * 60 * 60);
    /// // Roundtrip the time so that the assert below works.
    /// let t = SystemTime::from(Timestamp::try_from(t)?);
    ///
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(Some("Alice Lovelace <alice@example.org>"))
    ///         .set_creation_time(t)
    ///         .generate()?;
    /// assert_eq!(cert.primary_key().self_signatures().nth(0).unwrap()
    ///            .signature_creation_time(),
    ///            Some(t));
    /// # Ok(())
    /// # }
    /// ```
    pub fn set_creation_time<T>(mut self, creation_time: T) -> Self
        where T: Into<Option<std::time::SystemTime>>,
    {
        self.creation_time = creation_time.into();
        self
    }

    /// Returns the configured creation time, if any.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::time::SystemTime;
    ///
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let mut builder = CertBuilder::new();
    /// assert!(builder.creation_time().is_none());
    ///
    /// let now = std::time::SystemTime::now();
    /// builder = builder.set_creation_time(Some(now));
    /// assert_eq!(builder.creation_time(), Some(now));
    ///
    /// builder = builder.set_creation_time(None);
    /// assert!(builder.creation_time().is_none());
    /// # Ok(())
    /// # }
    /// ```
    pub fn creation_time(&self) -> Option<std::time::SystemTime>
    {
        self.creation_time
    }

    /// Sets the default asymmetric algorithms.
    ///
    /// This method controls the set of algorithms that is used to
    /// generate the certificate's keys.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::types::PublicKeyAlgorithm;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (ecc, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .set_cipher_suite(CipherSuite::Cv25519)
    ///         .generate()?;
    /// assert_eq!(ecc.primary_key().key().pk_algo(), PublicKeyAlgorithm::EdDSA);
    ///
    /// let (rsa, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .set_cipher_suite(CipherSuite::RSA2k)
    ///         .generate()?;
    /// assert_eq!(rsa.primary_key().key().pk_algo(), PublicKeyAlgorithm::RSAEncryptSign);
    /// # Ok(())
    /// # }
    /// ```
    pub fn set_cipher_suite(mut self, cs: CipherSuite) -> Self {
        self.ciphersuite = cs;
        self
    }

    /// Sets whether the certificate is exportable.
    ///
    /// This method controls whether the certificate is exportable.
    /// If the certificate builder is configured to make a
    /// non-exportable certificate, then all the signatures that it
    /// creates include the [Exportable Certification] subpacket
    /// that is set to `false`.
    ///
    /// [Exportable Certification]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.19
    ///
    /// # Examples
    ///
    /// When exporting a non-exportable certificate, nothing will be
    /// exported.  This is also the case when the output is ASCII
    /// armored.
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::parse::Parse;
    /// use openpgp::serialize::Serialize;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (cert, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .set_exportable(false)
    ///         .generate()?;
    /// let mut exported = Vec::new();
    /// cert.armored().export(&mut exported)?;
    ///
    /// let certs = CertParser::from_bytes(&exported)?
    ///     .collect::<Result<Vec<Cert>>>()?;
    /// assert_eq!(certs.len(), 0);
    /// assert_eq!(exported.len(), 0, "{}", String::from_utf8_lossy(&exported));
    /// # Ok(())
    /// # }
    /// ```
    pub fn set_exportable(mut self, exportable: bool) -> Self {
        self.exportable = exportable;
        self
    }

    /// Sets the version of OpenPGP to generate keys for.
    ///
    /// Supported are [`Profile::RFC9580`] which will generate version
    /// 6 keys, and [`Profile::RFC4880`] which will version 4 keys.
    /// By default, we generate version 6 keys.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (key, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .set_profile(openpgp::Profile::RFC9580)?
    ///         .generate()?;
    /// assert_eq!(key.primary_key().key().version(), 6);
    ///
    /// let (key, _) =
    ///     CertBuilder::general_purpose(Some("bob@example.org"))
    ///         .set_profile(openpgp::Profile::RFC4880)?
    ///         .generate()?;
    /// assert_eq!(key.primary_key().key().version(), 4);
    /// # Ok(())
    /// # }
    /// ```
    pub fn set_profile(mut self, profile: Profile) -> Result<Self> {
        self.profile = profile;
        Ok(self)
    }

    /// Sets the features the certificate will advertise.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::types::Features;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let (key, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .set_profile(openpgp::Profile::RFC9580)?
    ///         .generate()?;
    /// assert_eq!(key.primary_key().key().version(), 6);
    ///
    /// // Bob is old-school: he needs this key to work on an old
    /// // phone.  Therefore, he doesn't want to get SEIPDv2 messages,
    /// // which his phone doesn't handle.  He advertises support for
    /// // SEIPDv1 only.
    /// let (key, _) =
    ///     CertBuilder::general_purpose(Some("bob@example.org"))
    ///         .set_profile(openpgp::Profile::RFC4880)?
    ///         .set_features(Features::empty().set_seipdv1())?
    ///         .generate()?;
    /// assert_eq!(key.primary_key().key().version(), 4);
    /// # Ok(())
    /// # }
    /// ```
    pub fn set_features(mut self, features: Features) -> Result<Self> {
        self.features = features;
        Ok(self)
    }

    /// Adds a User ID.
    ///
    /// Adds a User ID to the certificate.  The first User ID that is
    /// added, whether via this interface or another interface, e.g.,
    /// [`CertBuilder::general_purpose`], will have the [primary User
    /// ID flag] set.
    ///
    /// [`CertBuilder::general_purpose`]: CertBuilder::general_purpose()
    /// [primary User ID flag]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.27
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::packet::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(Some("Alice Lovelace <alice@example.org>"))
    ///         .add_userid("Alice Lovelace <alice@lovelace.name>")
    ///         .generate()?;
    ///
    /// assert_eq!(cert.userids().count(), 2);
    /// let mut userids = cert.with_policy(p, None)?.userids().collect::<Vec<_>>();
    /// // Sort lexicographically.
    /// userids.sort_by(|a, b| a.userid().value().cmp(b.userid().value()));
    /// assert_eq!(userids[0].userid(),
    ///            &UserID::from("Alice Lovelace <alice@example.org>"));
    /// assert_eq!(userids[1].userid(),
    ///            &UserID::from("Alice Lovelace <alice@lovelace.name>"));
    ///
    ///
    /// assert_eq!(userids[0].binding_signature().primary_userid().unwrap_or(false), true);
    /// assert_eq!(userids[1].binding_signature().primary_userid().unwrap_or(false), false);
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_userid<U>(mut self, uid: U) -> Self
        where U: Into<packet::UserID>
    {
        self.userids.push((None, uid.into()));
        self
    }

    /// Adds a User ID with a binding signature based on `builder`.
    ///
    /// Adds a User ID to the certificate, creating the binding
    /// signature using `builder`.  The `builder`s signature type must
    /// be a certification signature (i.e. either
    /// [`GenericCertification`], [`PersonaCertification`],
    /// [`CasualCertification`], or [`PositiveCertification`]).
    ///
    /// The key generation step uses `builder` as a template, but
    /// tweaks it so the signature is a valid binding signature.  If
    /// you need more control, consider using
    /// [`UserID::bind`](crate::packet::UserID::bind).
    ///
    /// The following modifications are performed on `builder`:
    ///
    ///   - An appropriate hash algorithm is selected.
    ///
    ///   - The creation time is set.
    ///
    ///   - Primary key metadata is added (key flags, key validity period).
    ///
    ///   - Certificate metadata is added (feature flags, algorithm
    ///     preferences).
    ///
    ///   - The [`CertBuilder`] marks exactly one User ID or User
    ///     Attribute as primary: The first one provided to
    ///     [`CertBuilder::add_userid_with`] or
    ///     [`CertBuilder::add_user_attribute_with`] (the UserID takes
    ///     precedence) that is marked as primary, or the first User
    ///     ID or User Attribute added to the [`CertBuilder`].
    ///
    ///   [`GenericCertification`]: crate::types::SignatureType::GenericCertification
    ///   [`PersonaCertification`]: crate::types::SignatureType::PersonaCertification
    ///   [`CasualCertification`]: crate::types::SignatureType::CasualCertification
    ///   [`PositiveCertification`]: crate::types::SignatureType::PositiveCertification
    ///   [primary User ID flag]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.27
    ///
    /// # Examples
    ///
    /// This example very casually binds a User ID to a certificate.
    ///
    /// ```
    /// # fn main() -> sequoia_openpgp::Result<()> {
    /// # use sequoia_openpgp as openpgp;
    /// # use openpgp::cert::prelude::*;
    /// # use openpgp::packet::{prelude::*, signature::subpacket::*};
    /// # use openpgp::policy::StandardPolicy;
    /// # use openpgp::types::*;
    /// # let policy = &StandardPolicy::new();
    /// #
    /// let (cert, revocation_cert) =
    ///     CertBuilder::general_purpose(
    ///         Some("Alice Lovelace <alice@example.org>"))
    ///     .add_userid_with(
    ///         "trinity",
    ///         SignatureBuilder::new(SignatureType::CasualCertification)
    ///             .set_notation("rabbit@example.org", b"follow me",
    ///                           NotationDataFlags::empty().set_human_readable(),
    ///                           false)?)?
    ///     .generate()?;
    ///
    /// assert_eq!(cert.userids().count(), 2);
    /// let mut userids = cert.with_policy(policy, None)?.userids().collect::<Vec<_>>();
    /// // Sort lexicographically.
    /// userids.sort_by(|a, b| a.userid().value().cmp(b.userid().value()));
    /// assert_eq!(userids[0].userid(),
    ///            &UserID::from("Alice Lovelace <alice@example.org>"));
    /// assert_eq!(userids[1].userid(),
    ///            &UserID::from("trinity"));
    ///
    /// assert!(userids[0].binding_signature().primary_userid().unwrap_or(false));
    /// assert!(! userids[1].binding_signature().primary_userid().unwrap_or(false));
    /// assert_eq!(userids[1].binding_signature().notation("rabbit@example.org")
    ///            .next().unwrap(), b"follow me");
    /// # Ok(()) }
    /// ```
    pub fn add_userid_with<U, B>(mut self, uid: U, builder: B)
                                 -> Result<Self>
    where U: Into<packet::UserID>,
          B: Into<SignatureBuilder>,
    {
        let builder = builder.into();
        match builder.typ() {
            SignatureType::GenericCertification
                | SignatureType::PersonaCertification
                | SignatureType::CasualCertification
                | SignatureType::PositiveCertification =>
            {
                self.userids.push((Some(builder), uid.into()));
                Ok(self)
            },
            t =>
                Err(Error::InvalidArgument(format!(
                    "Signature type is not a certification: {}", t)).into()),
        }
    }

    /// Adds a new User Attribute.
    ///
    /// Adds a User Attribute to the certificate.  If there are no
    /// User IDs, the first User attribute that is added, whether via
    /// this interface or another interface, will have the [primary
    /// User ID flag] set.
    ///
    /// [primary User ID flag]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.27
    ///
    /// # Examples
    ///
    /// When there are no User IDs, the first User Attribute has the
    /// primary User ID flag set:
    ///
    /// ```
    /// # use openpgp::packet::user_attribute::Subpacket;
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::packet::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    /// #
    /// # // Create some user attribute. Doctests do not pass cfg(test),
    /// # // so UserAttribute::arbitrary is not available
    /// # let sp = Subpacket::Unknown(7, vec![7; 7].into_boxed_slice());
    /// # let user_attribute = UserAttribute::new(&[sp])?;
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_user_attribute(user_attribute)
    ///         .generate()?;
    ///
    /// assert_eq!(cert.userids().count(), 0);
    /// assert_eq!(cert.user_attributes().count(), 1);
    /// let mut uas = cert.with_policy(p, None)?.user_attributes().collect::<Vec<_>>();
    /// assert_eq!(uas[0].binding_signature().primary_userid().unwrap_or(false), true);
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Where there are User IDs, then the primary User ID flag is not
    /// set:
    ///
    /// ```
    /// # use openpgp::packet::user_attribute::Subpacket;
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::packet::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    /// #
    /// # // Create some user attribute. Doctests do not pass cfg(test),
    /// # // so UserAttribute::arbitrary is not available
    /// # let sp = Subpacket::Unknown(7, vec![7; 7].into_boxed_slice());
    /// # let user_attribute = UserAttribute::new(&[sp])?;
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_userid("alice@example.org")
    ///         .add_user_attribute(user_attribute)
    ///         .generate()?;
    ///
    /// assert_eq!(cert.userids().count(), 1);
    /// assert_eq!(cert.user_attributes().count(), 1);
    /// let mut uas = cert.with_policy(p, None)?.user_attributes().collect::<Vec<_>>();
    /// assert_eq!(uas[0].binding_signature().primary_userid().unwrap_or(false), false);
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_user_attribute<U>(mut self, ua: U) -> Self
        where U: Into<packet::UserAttribute>
    {
        self.user_attributes.push((None, ua.into()));
        self
    }

    /// Adds a User Attribute with a binding signature based on `builder`.
    ///
    /// Adds a User Attribute to the certificate, creating the binding
    /// signature using `builder`.  The `builder`s signature type must
    /// be a certification signature (i.e. either
    /// [`GenericCertification`], [`PersonaCertification`],
    /// [`CasualCertification`], or [`PositiveCertification`]).
    ///
    /// The key generation step uses `builder` as a template, but
    /// tweaks it so the signature is a valid binding signature.  If
    /// you need more control, consider using
    /// [`UserAttribute::bind`](crate::packet::UserAttribute::bind).
    ///
    /// The following modifications are performed on `builder`:
    ///
    ///   - An appropriate hash algorithm is selected.
    ///
    ///   - The creation time is set.
    ///
    ///   - Primary key metadata is added (key flags, key validity period).
    ///
    ///   - Certificate metadata is added (feature flags, algorithm
    ///     preferences).
    ///
    ///   - The [`CertBuilder`] marks exactly one User ID or User
    ///     Attribute as primary: The first one provided to
    ///     [`CertBuilder::add_userid_with`] or
    ///     [`CertBuilder::add_user_attribute_with`] (the UserID takes
    ///     precedence) that is marked as primary, or the first User
    ///     ID or User Attribute added to the [`CertBuilder`].
    ///
    ///   [`GenericCertification`]: crate::types::SignatureType::GenericCertification
    ///   [`PersonaCertification`]: crate::types::SignatureType::PersonaCertification
    ///   [`CasualCertification`]: crate::types::SignatureType::CasualCertification
    ///   [`PositiveCertification`]: crate::types::SignatureType::PositiveCertification
    ///   [primary User ID flag]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.27
    ///
    /// # Examples
    ///
    /// This example very casually binds a user attribute to a
    /// certificate.
    ///
    /// ```
    /// # fn main() -> sequoia_openpgp::Result<()> {
    /// # use sequoia_openpgp as openpgp;
    /// # use openpgp::packet::user_attribute::Subpacket;
    /// # use openpgp::cert::prelude::*;
    /// # use openpgp::packet::{prelude::*, signature::subpacket::*};
    /// # use openpgp::policy::StandardPolicy;
    /// # use openpgp::types::*;
    /// #
    /// # let policy = &StandardPolicy::new();
    /// #
    /// # // Create some user attribute. Doctests do not pass cfg(test),
    /// # // so UserAttribute::arbitrary is not available
    /// # let user_attribute =
    /// #   UserAttribute::new(&[Subpacket::Unknown(7, vec![7; 7].into())])?;
    /// let (cert, revocation_cert) =
    ///     CertBuilder::general_purpose(
    ///         Some("Alice Lovelace <alice@example.org>"))
    ///     .add_user_attribute_with(
    ///         user_attribute,
    ///         SignatureBuilder::new(SignatureType::CasualCertification)
    ///             .set_notation("rabbit@example.org", b"follow me",
    ///                           NotationDataFlags::empty().set_human_readable(),
    ///                           false)?)?
    ///     .generate()?;
    ///
    /// let uas = cert.with_policy(policy, None)?.user_attributes().collect::<Vec<_>>();
    /// assert_eq!(uas.len(), 1);
    /// assert!(! uas[0].binding_signature().primary_userid().unwrap_or(false));
    /// assert_eq!(uas[0].binding_signature().notation("rabbit@example.org")
    ///            .next().unwrap(), b"follow me");
    /// # Ok(()) }
    /// ```
    pub fn add_user_attribute_with<U, B>(mut self, ua: U, builder: B)
                                         -> Result<Self>
    where U: Into<packet::UserAttribute>,
          B: Into<SignatureBuilder>,
    {
        let builder = builder.into();
        match builder.typ() {
            SignatureType::GenericCertification
                | SignatureType::PersonaCertification
                | SignatureType::CasualCertification
                | SignatureType::PositiveCertification =>
            {
                self.user_attributes.push((Some(builder), ua.into()));
                Ok(self)
            },
            t =>
                Err(Error::InvalidArgument(format!(
                    "Signature type is not a certification: {}", t)).into()),
        }
    }

    /// Adds a signing-capable subkey.
    ///
    /// The key uses the default cipher suite (see
    /// [`CertBuilder::set_cipher_suite`]), and is not set to expire.
    /// Use [`CertBuilder::add_subkey`] if you need to change these
    /// parameters.
    ///
    /// [`CertBuilder::set_cipher_suite`]: CertBuilder::set_cipher_suite()
    /// [`CertBuilder::add_subkey`]: CertBuilder::add_subkey()
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_signing_subkey()
    ///         .generate()?;
    ///
    /// assert_eq!(cert.keys().count(), 2);
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty().set_signing()));
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_signing_subkey(self) -> Self {
        self.add_subkey(KeyFlags::empty().set_signing(), None, None)
    }

    /// Adds a subkey suitable for transport encryption.
    ///
    /// The key uses the default cipher suite (see
    /// [`CertBuilder::set_cipher_suite`]), and is not set to expire.
    /// Use [`CertBuilder::add_subkey`] if you need to change these
    /// parameters.
    ///
    /// [`CertBuilder::set_cipher_suite`]: CertBuilder::set_cipher_suite()
    /// [`CertBuilder::add_subkey`]: CertBuilder::add_subkey()
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_transport_encryption_subkey()
    ///         .generate()?;
    ///
    /// assert_eq!(cert.keys().count(), 2);
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty().set_transport_encryption()));
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_transport_encryption_subkey(self) -> Self {
        self.add_subkey(KeyFlags::empty().set_transport_encryption(),
                        None, None)
    }

    /// Adds a subkey suitable for storage encryption.
    ///
    /// The key uses the default cipher suite (see
    /// [`CertBuilder::set_cipher_suite`]), and is not set to expire.
    /// Use [`CertBuilder::add_subkey`] if you need to change these
    /// parameters.
    ///
    /// [`CertBuilder::set_cipher_suite`]: CertBuilder::set_cipher_suite()
    /// [`CertBuilder::add_subkey`]: CertBuilder::add_subkey()
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_storage_encryption_subkey()
    ///         .generate()?;
    ///
    /// assert_eq!(cert.keys().count(), 2);
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty().set_storage_encryption()));
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_storage_encryption_subkey(self) -> Self {
        self.add_subkey(KeyFlags::empty().set_storage_encryption(),
                        None, None)
    }

    /// Adds a certification-capable subkey.
    ///
    /// The key uses the default cipher suite (see
    /// [`CertBuilder::set_cipher_suite`]), and is not set to expire.
    /// Use [`CertBuilder::add_subkey`] if you need to change these
    /// parameters.
    ///
    /// [`CertBuilder::set_cipher_suite`]: CertBuilder::set_cipher_suite()
    /// [`CertBuilder::add_subkey`]: CertBuilder::add_subkey()
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_certification_subkey()
    ///         .generate()?;
    ///
    /// assert_eq!(cert.keys().count(), 2);
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty().set_certification()));
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_certification_subkey(self) -> Self {
        self.add_subkey(KeyFlags::empty().set_certification(), None, None)
    }

    /// Adds an authentication-capable subkey.
    ///
    /// The key uses the default cipher suite (see
    /// [`CertBuilder::set_cipher_suite`]), and is not set to expire.
    /// Use [`CertBuilder::add_subkey`] if you need to change these
    /// parameters.
    ///
    /// [`CertBuilder::set_cipher_suite`]: CertBuilder::set_cipher_suite()
    /// [`CertBuilder::add_subkey`]: CertBuilder::add_subkey()
    ///
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> openpgp::Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::new()
    ///         .add_authentication_subkey()
    ///         .generate()?;
    ///
    /// assert_eq!(cert.keys().count(), 2);
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty().set_authentication()));
    /// # Ok(())
    /// # }
    /// ```
    pub fn add_authentication_subkey(self) -> Self {
        self.add_subkey(KeyFlags::empty().set_authentication(), None, None)
    }

    /// Adds a custom subkey.
    ///
    /// If `validity` is `None`, the subkey will be valid for the same
    /// period as the primary key.
    ///
    /// Likewise, if `cs` is `None`, the same cipher suite is used as
    /// for the primary key.
    ///
    /// # Examples
    ///
    /// Generates a certificate with an encryption subkey that is for
    /// protecting *both* data in transit and data at rest, and
    /// expires at a different time from the primary key:
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let now = std::time::SystemTime::now();
    /// let y = std::time::Duration::new(365 * 24 * 60 * 60, 0);
    ///
    /// // Make the certificate expire in 2 years, and the subkey
    /// // expire in a year.
    /// let (cert,_) = CertBuilder::new()
    ///     .set_creation_time(now)
    ///     .set_validity_period(2 * y)
    ///     .add_subkey(KeyFlags::empty()
    ///                     .set_storage_encryption()
    ///                     .set_transport_encryption(),
    ///                 y,
    ///                 None)
    ///     .generate()?;
    ///
    /// assert_eq!(cert.with_policy(p, now)?.keys().alive().count(), 2);
    /// assert_eq!(cert.with_policy(p, now + y)?.keys().alive().count(), 1);
    /// assert_eq!(cert.with_policy(p, now + 2 * y)?.keys().alive().count(), 0);
    ///
    /// let ka = cert.with_policy(p, None)?.keys().nth(1).unwrap();
    /// assert_eq!(ka.key_flags(),
    ///            Some(KeyFlags::empty()
    ///                     .set_storage_encryption()
    ///                     .set_transport_encryption()));
    /// # Ok(()) }
    /// ```
    pub fn add_subkey<T, C>(mut self, flags: KeyFlags, validity: T, cs: C)
        -> Self
        where T: Into<Option<time::Duration>>,
              C: Into<Option<CipherSuite>>,
    {
        self.subkeys.push((None, KeyBlueprint {
            flags,
            validity: validity.into(),
            ciphersuite: cs.into(),
        }));
        self
    }

    /// Adds a subkey with a binding signature based on `builder`.
    ///
    /// Adds a subkey to the certificate, creating the binding
    /// signature using `builder`.  The `builder`s signature type must
    /// be [`SubkeyBinding`].
    ///
    /// The key generation step uses `builder` as a template, but adds
    /// all subpackets that the signature needs to be a valid binding
    /// signature.  If you need more control, or want to adopt
    /// existing keys, consider using
    /// [`Key::bind`](crate::packet::Key::bind).
    ///
    /// The following modifications are performed on `builder`:
    ///
    ///   - An appropriate hash algorithm is selected.
    ///
    ///   - The creation time is set.
    ///
    ///   - Key metadata is added (key flags, key validity period).
    ///
    ///   [`SubkeyBinding`]: crate::types::SignatureType::SubkeyBinding
    ///
    /// If `validity` is `None`, the subkey will be valid for the same
    /// period as the primary key.
    ///
    /// # Examples
    ///
    /// This example binds a signing subkey to a certificate,
    /// restricting its use to authentication of software.
    ///
    /// ```
    /// # fn main() -> sequoia_openpgp::Result<()> {
    /// # use sequoia_openpgp as openpgp;
    /// # use openpgp::packet::user_attribute::Subpacket;
    /// # use openpgp::cert::prelude::*;
    /// # use openpgp::packet::{prelude::*, signature::subpacket::*};
    /// # use openpgp::policy::StandardPolicy;
    /// # use openpgp::types::*;
    /// let (cert, revocation_cert) =
    ///     CertBuilder::general_purpose(
    ///         Some("Alice Lovelace <alice@example.org>"))
    ///     .add_subkey_with(
    ///         KeyFlags::empty().set_signing(), None, None,
    ///         SignatureBuilder::new(SignatureType::SubkeyBinding)
    ///             // Add a critical notation!
    ///             .set_notation("code-signing@policy.example.org", b"",
    ///                           NotationDataFlags::empty(), true)?)?
    ///     .generate()?;
    ///
    /// // Under the standard policy, the additional signing subkey
    /// // is not bound.
    /// let p = StandardPolicy::new();
    /// assert_eq!(cert.with_policy(&p, None)?.keys().for_signing().count(), 1);
    ///
    /// // However, software implementing the notation see the additional
    /// // signing subkey.
    /// let mut p = StandardPolicy::new();
    /// p.good_critical_notations(&["code-signing@policy.example.org"]);
    /// assert_eq!(cert.with_policy(&p, None)?.keys().for_signing().count(), 2);
    /// # Ok(()) }
    /// ```
    pub fn add_subkey_with<T, C, B>(mut self, flags: KeyFlags, validity: T,
                                    cs: C, builder: B) -> Result<Self>
        where T: Into<Option<time::Duration>>,
              C: Into<Option<CipherSuite>>,
              B: Into<SignatureBuilder>,
    {
        let builder = builder.into();
        match builder.typ() {
            SignatureType::SubkeyBinding => {
                self.subkeys.push((Some(builder), KeyBlueprint {
                    flags,
                    validity: validity.into(),
                    ciphersuite: cs.into(),
                }));
                Ok(self)
            },
            t =>
                Err(Error::InvalidArgument(format!(
                    "Signature type is not a subkey binding: {}", t)).into()),
        }
    }

    /// Sets the primary key's key flags.
    ///
    /// By default, the primary key is set to only be certification
    /// capable.  This allows the caller to set additional flags.
    ///
    /// # Examples
    ///
    /// Makes the primary key signing-capable but not
    /// certification-capable.
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::KeyFlags;
    ///
    /// # fn main() -> Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(Some("Alice Lovelace <alice@example.org>"))
    ///         .set_primary_key_flags(KeyFlags::empty().set_signing())
    ///         .generate()?;
    ///
    /// // Observe that the primary key's certification capability is
    /// // set implicitly.
    /// assert_eq!(cert.with_policy(p, None)?.primary_key().key_flags(),
    ///            Some(KeyFlags::empty().set_signing()));
    /// # Ok(()) }
    /// ```
    pub fn set_primary_key_flags(mut self, flags: KeyFlags) -> Self {
        self.primary.flags = flags;
        self
    }

    /// Sets a password to encrypt the secret keys with.
    ///
    /// The password is used to encrypt all secret key material.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    ///
    /// # fn main() -> Result<()> {
    /// // Make the certificate expire in 10 minutes.
    /// let (cert, rev) =
    ///     CertBuilder::general_purpose(Some("Alice Lovelace <alice@example.org>"))
    ///         .set_password(Some("1234".into()))
    ///         .generate()?;
    ///
    /// for ka in cert.keys() {
    ///     assert!(ka.key().has_secret());
    /// }
    /// # Ok(()) }
    /// ```
    pub fn set_password(mut self, password: Option<Password>) -> Self {
        self.password = password;
        self
    }

    /// Sets the certificate's validity period.
    ///
    /// The determines how long the certificate is valid.  That is,
    /// after the validity period, the certificate is considered to be
    /// expired.
    ///
    /// The validity period starts with the creation time (see
    /// [`CertBuilder::set_creation_time`]).
    ///
    /// A value of `None` means that the certificate never expires.
    ///
    /// See [this section](CertBuilder#expiration) of the type's
    /// documentation for security considerations of key expiration.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::RevocationKey;
    ///
    /// # fn main() -> Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let now = std::time::SystemTime::now();
    /// let s = std::time::Duration::new(1, 0);
    ///
    /// // Make the certificate expire in 10 minutes.
    /// let (cert,_) = CertBuilder::new()
    ///     .set_creation_time(now)
    ///     .set_validity_period(600 * s)
    ///     .generate()?;
    ///
    /// assert!(cert.with_policy(p, now)?.primary_key().alive().is_ok());
    /// assert!(cert.with_policy(p, now + 599 * s)?.primary_key().alive().is_ok());
    /// assert!(cert.with_policy(p, now + 600 * s)?.primary_key().alive().is_err());
    /// # Ok(()) }
    /// ```
    pub fn set_validity_period<T>(mut self, validity: T) -> Self
        where T: Into<Option<time::Duration>>
    {
        self.primary.validity = validity.into();
        self
    }

    /// Sets designated revokers.
    ///
    /// Adds designated revokers to the primary key.  This allows the
    /// designated revoker to issue revocation certificates on behalf
    /// of the primary key.
    ///
    /// # Examples
    ///
    /// Make Alice a designated revoker for Bob:
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::RevocationKey;
    ///
    /// # fn main() -> Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (alice, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .generate()?;
    /// let (bob, _) =
    ///     CertBuilder::general_purpose(Some("bob@example.org"))
    ///         .set_revocation_keys(vec![(&alice).into()])
    ///         .generate()?;
    ///
    /// // Make sure Alice is listed as a designated revoker for Bob.
    /// assert_eq!(bob.revocation_keys(p).collect::<Vec<&RevocationKey>>(),
    ///            vec![&(&alice).into()]);
    /// # Ok(()) }
    /// ```
    pub fn set_revocation_keys(mut self, revocation_keys: Vec<RevocationKey>)
        -> Self
    {
        self.revocation_keys = Some(revocation_keys);
        self
    }

    /// Generates a certificate.
    ///
    /// # Examples
    ///
    /// ```
    /// use sequoia_openpgp as openpgp;
    /// # use openpgp::Result;
    /// use openpgp::cert::prelude::*;
    /// use openpgp::policy::StandardPolicy;
    /// use openpgp::types::RevocationKey;
    ///
    /// # fn main() -> Result<()> {
    /// let p = &StandardPolicy::new();
    ///
    /// let (alice, _) =
    ///     CertBuilder::general_purpose(Some("alice@example.org"))
    ///         .generate()?;
    /// # Ok(()) }
    /// ```
    pub fn generate(mut self) -> Result<(Cert, Signature)> {
        use crate::Packet;
        use crate::types::ReasonForRevocation;

        let creation_time =
            self.creation_time.unwrap_or_else(|| {
                use crate::packet::signature::SIG_BACKDATE_BY;
                crate::now() -
                    time::Duration::new(SIG_BACKDATE_BY, 0)
            });

        // Generate & self-sign primary key.
        let (primary, sig, mut signer) = self.primary_key(creation_time)?;

        // Construct a skeleton cert.  We need this to bind the new
        // components to.
        let cert = Cert::try_from(vec![
            Packet::SecretKey({
                let mut primary = primary.clone();
                if let Some(ref password) = self.password {
                    let (k, mut secret) = primary.take_secret();
                    secret.encrypt_in_place(&k, password)?;
                    primary = k.add_secret(secret).0;
                }
                primary
            }),
        ])?;
        // We will, however, collect any signatures and components in
        // a separate vector, and only add them in the end, so that we
        // canonicalize the new certificate just once.
        let mut acc = vec![
            Packet::from(sig),
        ];

        // We want to mark exactly one User ID or Attribute as primary.
        // First, figure out whether one of the binding signature
        // templates have the primary flag set.
        let have_primary_user_thing = {
            let is_primary = |osig: &Option<SignatureBuilder>| -> bool {
                osig.as_ref().and_then(|s| s.primary_userid()).unwrap_or(false)
            };

            self.userids.iter().map(|(s, _)| s).any(is_primary)
                || self.user_attributes.iter().map(|(s, _)| s).any(is_primary)
        };
        let mut emitted_primary_user_thing = false;

        // Sign UserIDs.
        for (template, uid) in std::mem::take(&mut self.userids).into_iter() {
            let sig = template.unwrap_or_else(
                || SignatureBuilder::new(SignatureType::PositiveCertification));
            let sig = Self::signature_common(sig, creation_time,
                                             self.exportable)?;
            let mut sig = self.add_primary_key_metadata(sig)?;

            // Make sure we mark exactly one User ID or Attribute as
            // primary.
            if emitted_primary_user_thing {
                sig = sig.modify_hashed_area(|mut a| {
                    a.remove_all(SubpacketTag::PrimaryUserID);
                    Ok(a)
                })?;
            } else if have_primary_user_thing {
                // Check if this is the first explicitly selected
                // user thing.
                emitted_primary_user_thing |=
                    sig.primary_userid().unwrap_or(false);
            } else {
                // Implicitly mark the first as primary.
                sig = sig.set_primary_userid(true)?;
                emitted_primary_user_thing = true;
            }

            let signature = uid.bind(&mut signer, &cert, sig)?;
            acc.push(uid.into());
            acc.push(signature.into());
        }

        // Sign UserAttributes.
        for (template, ua) in std::mem::take(&mut self.user_attributes) {
            let sig = template.unwrap_or_else(
                || SignatureBuilder::new(SignatureType::PositiveCertification));
            let sig = Self::signature_common(
                sig, creation_time, self.exportable)?;
            let mut sig = self.add_primary_key_metadata(sig)?;

            // Make sure we mark exactly one User ID or Attribute as
            // primary.
            if emitted_primary_user_thing {
                sig = sig.modify_hashed_area(|mut a| {
                    a.remove_all(SubpacketTag::PrimaryUserID);
                    Ok(a)
                })?;
            } else if have_primary_user_thing {
                // Check if this is the first explicitly selected
                // user thing.
                emitted_primary_user_thing |=
                    sig.primary_userid().unwrap_or(false);
            } else {
                // Implicitly mark the first as primary.
                sig = sig.set_primary_userid(true)?;
                emitted_primary_user_thing = true;
            }

            let signature = ua.bind(&mut signer, &cert, sig)?;
            acc.push(ua.into());
            acc.push(signature.into());
        }

        // Sign subkeys.
        for (template, blueprint) in self.subkeys {
            let flags = &blueprint.flags;
            let mut subkey = blueprint.ciphersuite
                .unwrap_or(self.ciphersuite)
                .generate_key(flags, self.profile)?
                .role_into_subordinate();
            subkey.set_creation_time(creation_time)?;

            let sig = template.unwrap_or_else(
                || SignatureBuilder::new(SignatureType::SubkeyBinding));
            let sig = Self::signature_common(
                sig, creation_time, self.exportable)?;
            let mut builder = sig
                .set_key_flags(flags.clone())?
                .set_key_validity_period(blueprint.validity.or(self.primary.validity))?;

            if flags.for_certification() || flags.for_signing()
                || flags.for_authentication()
            {
                // We need to create a primary key binding signature.
                let mut subkey_signer = subkey.clone().into_keypair().unwrap();
                let backsig =
                    signature::SignatureBuilder::new(SignatureType::PrimaryKeyBinding)
                    .set_signature_creation_time(creation_time)?
                    // GnuPG wants at least a 512-bit hash for P521 keys.
                    .set_hash_algo(HashAlgorithm::SHA512)
                    .sign_primary_key_binding(&mut subkey_signer, &primary,
                                              &subkey)?;
                builder = builder.set_embedded_signature(backsig)?;
            }

            let signature = subkey.bind(&mut signer, &cert, builder)?;

            if let Some(ref password) = self.password {
                let (k, mut secret) = subkey.take_secret();
                secret.encrypt_in_place(&k, password)?;
                subkey = k.add_secret(secret).0;
            }
            acc.push(subkey.into());
            acc.push(signature.into());
        }

        // Now add the new components and canonicalize once.
        let cert = cert.insert_packets(acc)?.0;

        let revocation = CertRevocationBuilder::new()
            .set_signature_creation_time(creation_time)?
            .set_reason_for_revocation(
                ReasonForRevocation::Unspecified, b"Unspecified")?
            .build(&mut signer, &cert, None)?;

        // keys generated by the builder are never invalid
        assert!(cert.bad.is_empty());
        assert!(cert.unknowns.is_empty());

        Ok((cert, revocation))
    }

    /// Creates the primary key and a direct key signature.
    fn primary_key(&self, creation_time: std::time::SystemTime)
        -> Result<(KeySecretKey, Signature, Box<dyn Signer>)>
    {
        let mut key = self.primary.ciphersuite
            .unwrap_or(self.ciphersuite)
            .generate_key(KeyFlags::empty().set_certification(),
                          self.profile)?
            .role_into_primary();
        key.set_creation_time(creation_time)?;
        let sig = SignatureBuilder::new(SignatureType::DirectKey);
        let sig = Self::signature_common(
            sig, creation_time, self.exportable)?;
        let mut sig = self.add_primary_key_metadata(sig)?;

        if let Some(ref revocation_keys) = self.revocation_keys {
            for k in revocation_keys.into_iter().cloned() {
                sig = sig.add_revocation_key(k)?;
            }
        }

        let mut signer = key.clone().into_keypair()
            .expect("key generated above has a secret");
        let sig = sig.sign_direct_key(&mut signer, key.parts_as_public())?;

        Ok((key, sig, Box::new(signer)))
    }

    /// Common settings for generated signatures.
    fn signature_common(builder: SignatureBuilder,
                        creation_time: time::SystemTime,
                        exportable: bool)
                        -> Result<SignatureBuilder>
    {
        let mut builder = builder
            // GnuPG wants at least a 512-bit hash for P521 keys.
            .set_hash_algo(HashAlgorithm::SHA512)
            .set_signature_creation_time(creation_time)?;
        if ! exportable {
            builder = builder.set_exportable_certification(false)?;
        }
        Ok(builder)
    }


    /// Adds primary key metadata to the signature.
    fn add_primary_key_metadata(&self,
                                builder: SignatureBuilder)
                                -> Result<SignatureBuilder>
    {
        builder
            .set_features(self.features.clone())?
            .set_key_flags(self.primary.flags.clone())?
            .set_key_validity_period(self.primary.validity)?
            .set_preferred_hash_algorithms(vec![
                HashAlgorithm::SHA512,
                HashAlgorithm::SHA256,
            ])?
            .set_preferred_symmetric_algorithms(vec![
                SymmetricAlgorithm::AES256,
                SymmetricAlgorithm::AES128,
            ])
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use super::*;
    use crate::Fingerprint;
    use crate::crypto::mpi::PublicKey;
    use crate::packet::signature::subpacket::{SubpacketTag, SubpacketValue};
    use crate::types::Curve;
    use crate::types::PublicKeyAlgorithm;
    use crate::parse::Parse;
    use crate::policy::StandardPolicy as P;
    use crate::serialize::Serialize;

    #[test]
    fn all_opts() {
        let p = &P::new();

        let (cert, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .add_userid("test1@example.com")
            .add_userid("test2@example.com")
            .add_signing_subkey()
            .add_transport_encryption_subkey()
            .add_certification_subkey()
            .generate().unwrap();

        let mut userids = cert.userids().with_policy(p, None)
            .map(|u| String::from_utf8_lossy(u.userid().value()).into_owned())
            .collect::<Vec<String>>();
        userids.sort();

        assert_eq!(userids,
                   &[ "test1@example.com",
                      "test2@example.com",
                   ][..]);
        assert_eq!(cert.subkeys().count(), 3);
    }

    #[test]
    fn direct_key_sig() {
        let p = &P::new();

        let (cert, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .add_signing_subkey()
            .add_transport_encryption_subkey()
            .add_certification_subkey()
            .generate().unwrap();

        assert_eq!(cert.userids().count(), 0);
        assert_eq!(cert.subkeys().count(), 3);
        let sig =
            cert.primary_key().with_policy(p, None).unwrap().binding_signature();
        assert_eq!(sig.typ(), crate::types::SignatureType::DirectKey);
        assert!(sig.features().unwrap().supports_seipdv1());
    }

    #[test]
    fn setter() {
        let (cert1, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .set_cipher_suite(CipherSuite::RSA3k)
            .set_cipher_suite(CipherSuite::Cv25519)
            .generate().unwrap();
        assert_eq!(cert1.primary_key().key().pk_algo(), PublicKeyAlgorithm::EdDSA);

        let (cert2, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::RSA3k)
            .add_userid("test2@example.com")
            .add_transport_encryption_subkey()
            .generate().unwrap();
        assert_eq!(cert2.primary_key().key().pk_algo(),
                   PublicKeyAlgorithm::RSAEncryptSign);
        assert_eq!(cert2.subkeys().next().unwrap().key().pk_algo(),
                   PublicKeyAlgorithm::RSAEncryptSign);
    }

    #[test]
    fn defaults() {
        let p = &P::new();
        let (cert1, _) = CertBuilder::new()
            .add_userid("test2@example.com")
            .generate().unwrap();
        assert_eq!(cert1.primary_key().key().pk_algo(),
                   PublicKeyAlgorithm::EdDSA);
        assert!(cert1.subkeys().next().is_none());
        assert!(cert1.with_policy(p, None).unwrap().primary_userid().unwrap()
                .binding_signature().features().unwrap().supports_seipdv1());
    }

    #[test]
    fn not_always_certify() {
        let p = &P::new();
        let (cert1, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .set_primary_key_flags(KeyFlags::empty())
            .add_transport_encryption_subkey()
            .generate().unwrap();
        assert!(! cert1.primary_key().with_policy(p, None).unwrap().for_certification());
        assert_eq!(cert1.keys().subkeys().count(), 1);
    }

    #[test]
    fn gen_wired_subkeys() {
        let (cert1, _) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .set_primary_key_flags(KeyFlags::empty())
            .add_subkey(KeyFlags::empty().set_certification(), None, None)
            .generate().unwrap();
        let sig_pkts = cert1.subkeys().next().unwrap().bundle()
            .self_signatures().next().unwrap().hashed_area();

        match sig_pkts.subpacket(SubpacketTag::KeyFlags).unwrap().value() {
            SubpacketValue::KeyFlags(ref ks) => assert!(ks.for_certification()),
            v => panic!("Unexpected subpacket: {:?}", v),
        }

        assert_eq!(cert1.subkeys().count(), 1);
    }

    #[test]
    fn generate_revocation_certificate() {
        let p = &P::new();
        use crate::types::RevocationStatus;
        let (cert, revocation) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .generate().unwrap();
        assert_eq!(cert.revocation_status(p, None),
                   RevocationStatus::NotAsFarAsWeKnow);

        let cert = cert.insert_packets(revocation.clone()).unwrap().0;
        assert_eq!(cert.revocation_status(p, None),
                   RevocationStatus::Revoked(vec![ &revocation ]));
    }

    #[test]
    fn builder_roundtrip() {
        use std::convert::TryFrom;

        let (cert,_) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .add_signing_subkey()
            .generate().unwrap();
        let pile = cert.clone().into_packet_pile().into_children().collect::<Vec<_>>();
        let exp = Cert::try_from(pile).unwrap();

        assert_eq!(cert, exp);
    }

    #[test]
    fn encrypted_secrets() {
        let (cert,_) = CertBuilder::new()
            .set_cipher_suite(CipherSuite::Cv25519)
            .set_password(Some(String::from("streng geheim").into()))
            .generate().unwrap();
        assert!(cert.primary_key().key().optional_secret().unwrap().is_encrypted());
    }

    #[test]
    fn all_ciphersuites() {
        for cs in CipherSuite::variants()
            .into_iter().filter(|cs| cs.is_supported().is_ok())
        {
            assert!(CertBuilder::new()
                .set_cipher_suite(cs)
                .generate().is_ok());
        }
    }

    #[test]
    fn validity_periods() {
        let p = &P::new();

        let now = crate::now();
        let s = std::time::Duration::new(1, 0);

        let (cert,_) = CertBuilder::new()
            .set_creation_time(now)
            .set_validity_period(600 * s)
            .add_subkey(KeyFlags::empty().set_signing(),
                        300 * s, None)
            .add_subkey(KeyFlags::empty().set_authentication(),
                        None, None)
            .generate().unwrap();

        let key = cert.primary_key().key();
        let sig = &cert.primary_key().bundle().self_signatures().next().unwrap();
        assert!(sig.key_alive(key, now).is_ok());
        assert!(sig.key_alive(key, now + 590 * s).is_ok());
        assert!(! sig.key_alive(key, now + 610 * s).is_ok());

        let ka = cert.keys().with_policy(p, now).alive().revoked(false)
            .for_signing().next().unwrap();
        assert!(ka.alive().is_ok());
        assert!(ka.clone().with_policy(p, now + 290 * s).unwrap().alive().is_ok());
        assert!(! ka.clone().with_policy(p, now + 310 * s).unwrap().alive().is_ok());

        let ka = cert.keys().with_policy(p, now).alive().revoked(false)
            .for_authentication().next().unwrap();
        assert!(ka.alive().is_ok());
        assert!(ka.clone().with_policy(p, now + 590 * s).unwrap().alive().is_ok());
        assert!(! ka.clone().with_policy(p, now + 610 * s).unwrap().alive().is_ok());
    }

    #[test]
    fn creation_time() {
        let p = &P::new();

        use std::time::UNIX_EPOCH;
        let (cert, rev) = CertBuilder::new()
            .set_creation_time(UNIX_EPOCH)
            .set_cipher_suite(CipherSuite::Cv25519)
            .add_userid("foo")
            .add_signing_subkey()
            .generate().unwrap();

        assert_eq!(cert.primary_key().key().creation_time(), UNIX_EPOCH);
        assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
                   .binding_signature()
                   .signature_creation_time().unwrap(), UNIX_EPOCH);
        assert_eq!(cert.primary_key().with_policy(p, None).unwrap()
                   .direct_key_signature().unwrap()
                   .signature_creation_time().unwrap(), UNIX_EPOCH);
        assert_eq!(rev.signature_creation_time().unwrap(), UNIX_EPOCH);

        // (Sub)Keys.
        assert_eq!(cert.keys().with_policy(p, None).count(), 2);
        for ka in cert.keys().with_policy(p, None) {
            assert_eq!(ka.key().creation_time(), UNIX_EPOCH);
            assert_eq!(ka.binding_signature()
                       .signature_creation_time().unwrap(), UNIX_EPOCH);
        }

        // UserIDs.
        assert_eq!(cert.userids().count(), 1);
        for ui in cert.userids().with_policy(p, None) {
            assert_eq!(ui.binding_signature()
                       .signature_creation_time().unwrap(), UNIX_EPOCH);
        }
    }

    #[test]
    fn designated_revokers() -> Result<()> {
        use std::collections::HashSet;

        let p = &P::new();

        let fpr1 = "C03F A641 1B03 AE12 5764  6118 7223 B566 78E0 2528";
        let fpr2 = "50E6 D924 308D BF22 3CFB  510A C2B8 1905 6C65 2598";
        let revokers = vec![
            RevocationKey::new(PublicKeyAlgorithm::RSAEncryptSign,
                               Fingerprint::from_str(fpr1)?,
                               false),
            RevocationKey::new(PublicKeyAlgorithm::ECDSA,
                               Fingerprint::from_str(fpr2)?,
                               false)
        ];

        let (cert,_)
            = CertBuilder::general_purpose(Some("alice@example.org"))
            .set_revocation_keys(revokers.clone())
            .generate()?;
        let cert = cert.with_policy(p, None)?;

        assert_eq!(cert.revocation_keys().collect::<HashSet<_>>(),
                   revokers.iter().collect::<HashSet<_>>());

        // Do it again, with a key that has no User IDs.
        let (cert,_) = CertBuilder::new()
            .set_revocation_keys(revokers.clone())
            .generate()?;
        let cert = cert.with_policy(p, None)?;
        assert!(cert.primary_userid().is_err());

        assert_eq!(cert.revocation_keys().collect::<HashSet<_>>(),
                   revokers.iter().collect::<HashSet<_>>());

        // The designated revokers on all signatures should be
        // considered.
        let now = crate::types::Timestamp::now();
        let then = now.checked_add(crate::types::Duration::days(1)?).unwrap();
        let (cert,_) = CertBuilder::new()
            .set_revocation_keys(revokers.clone())
            .set_creation_time(now)
            .generate()?;

        // Add a newer direct key signature.
        use crate::crypto::hash::Hash;
        let mut primary_signer =
            cert.primary_key().key().clone().parts_into_secret()?
            .into_keypair()?;
        let mut hash = HashAlgorithm::SHA512.context()?
            .for_signature(primary_signer.public().version());
        cert.primary_key().key().hash(&mut hash)?;
        let sig = signature::SignatureBuilder::new(SignatureType::DirectKey)
            .set_signature_creation_time(then)?
            .sign_hash(&mut primary_signer, hash)?;
        let cert = cert.insert_packets(sig)?.0;

        assert!(cert.with_policy(p, then)?.primary_userid().is_err());
        assert_eq!(cert.revocation_keys(p).collect::<HashSet<_>>(),
                   revokers.iter().collect::<HashSet<_>>());
        Ok(())
    }

    /// Checks that the builder emits exactly one user id or attribute
    /// marked as primary.
    #[test]
    fn primary_user_things() -> Result<()> {
        fn count_primary_user_things(c: Cert) -> usize {
            c.into_packets().map(|p| match p {
                Packet::Signature(s) if s.primary_userid().unwrap_or(false)
                    => 1,
                _ => 0,
            }).sum()
        }

        use crate::packet::{prelude::*, user_attribute::Subpacket};
        let ua_foo =
            UserAttribute::new(&[Subpacket::Unknown(7, vec![7; 7].into())])?;
        let ua_bar =
            UserAttribute::new(&[Subpacket::Unknown(11, vec![11; 11].into())])?;

        let p = &P::new();
        let positive = SignatureType::PositiveCertification;

        let (c, _) = CertBuilder::new().generate()?;
        assert_eq!(count_primary_user_things(c), 0);

        let (c, _) = CertBuilder::new()
            .add_userid("foo")
            .generate()?;
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_userid("foo")
            .add_userid("bar")
            .generate()?;
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_user_attribute(ua_foo.clone())
            .generate()?;
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_user_attribute(ua_foo.clone())
            .add_user_attribute(ua_bar.clone())
            .generate()?;
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_userid("foo")
            .add_user_attribute(ua_foo.clone())
            .generate()?;
        let vc = c.with_policy(p, None)?;
        assert_eq!(vc.primary_userid()?.binding_signature().primary_userid(),
                   Some(true));
        assert_eq!(vc.primary_user_attribute()?.binding_signature().primary_userid(),
                   None);
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_user_attribute(ua_foo.clone())
            .add_userid("foo")
            .generate()?;
        let vc = c.with_policy(p, None)?;
        assert_eq!(vc.primary_userid()?.binding_signature().primary_userid(),
                   Some(true));
        assert_eq!(vc.primary_user_attribute()?.binding_signature().primary_userid(),
                   None);
        assert_eq!(count_primary_user_things(c), 1);

        let (c, _) = CertBuilder::new()
            .add_userid("foo")
            .add_userid_with(
                "buz",
                SignatureBuilder::new(positive).set_primary_userid(false)?)?
            .add_userid_with(
                "bar",
                SignatureBuilder::new(positive).set_primary_userid(true)?)?
            .add_userid_with(
                "baz",
                SignatureBuilder::new(positive).set_primary_userid(true)?)?
            .generate()?;
        let vc = c.with_policy(p, None)?;
        assert_eq!(vc.primary_userid()?.userid().value(), b"bar");
        assert_eq!(count_primary_user_things(c), 1);

        Ok(())
    }

    #[test]
    fn non_exportable_cert() -> Result<()> {
        // Make sure that when we export a non-exportable cert,
        // nothing is exported.

        let (cert, _) =
            CertBuilder::general_purpose(Some("alice@example.org"))
            .set_exportable(false)
            .generate()?;

        let (bob, _) =
            CertBuilder::general_purpose(Some("bob@example.org"))
            .generate()?;

        // Have Bob certify Alice's primary User ID with an exportable
        // signature.  This shouldn't make Alice's certificate
        // exportable.
        let mut keypair = bob.primary_key().key().clone()
            .parts_into_secret()?.into_keypair()?;
        let certification = cert.userids().nth(0).unwrap()
            .userid()
            .certify(&mut keypair, &cert,
                     SignatureType::PositiveCertification,
                     None, None)?;
        let cert = cert.insert_packets(certification)?.0;

        macro_rules! check {
            ($cert: expr, $export: ident, $expected: expr) => {
                let mut exported = Vec::new();
                $cert.$export(&mut exported)?;

                let certs = CertParser::from_bytes(&exported)?
                    .collect::<Result<Vec<Cert>>>()?;

                assert_eq!(certs.len(), $expected);

                if $expected == 0 {
                    assert_eq!(exported.len(), 0,
                               "{}", String::from_utf8_lossy(&exported));
                } else {
                    assert!(exported.len() > 0);
                }
            }
        }

        // Binary cert:
        check!(cert, export, 0);
        check!(cert, serialize, 1);

        // Binary TSK:
        check!(cert.as_tsk(), export, 0);
        check!(cert.as_tsk(), serialize, 1);

        // Armored cert:
        check!(cert.armored(), export, 0);
        check!(cert.armored(), serialize, 1);

        // Armored TSK:
        check!(cert.as_tsk().armored(), export, 0);
        check!(cert.as_tsk().armored(), serialize, 1);

        // Have Alice add an exportable self signature.  Now her
        // certificate should be exportable.
        let mut keypair = cert.primary_key().key().clone()
            .parts_into_secret()?.into_keypair()?;
        let certification = cert.userids().nth(0).unwrap()
            .userid()
            .certify(&mut keypair, &cert,
                     SignatureType::PositiveCertification,
                     None, None)?;
        let cert = cert.insert_packets(certification)?.0;

        macro_rules! check {
            ($cert: expr, $export: ident, $expected: expr) => {
                let mut exported = Vec::new();
                $cert.$export(&mut exported)?;

                let certs = CertParser::from_bytes(&exported)?
                    .collect::<Result<Vec<Cert>>>()?;

                assert_eq!(certs.len(), $expected);

                if $expected == 0 {
                    assert_eq!(exported.len(), 0,
                               "{}", String::from_utf8_lossy(&exported));
                } else {
                    assert!(exported.len() > 0);
                }
            }
        }

        // Binary cert:
        check!(cert, export, 1);
        check!(cert, serialize, 1);

        // Binary TSK:
        check!(cert.as_tsk(), export, 1);
        check!(cert.as_tsk(), serialize, 1);

        // Armored cert:
        check!(cert.armored(), export, 1);
        check!(cert.armored(), serialize, 1);

        // Armored TSK:
        check!(cert.as_tsk().armored(), export, 1);
        check!(cert.as_tsk().armored(), serialize, 1);

        Ok(())
    }

    #[test]
    fn check_algos() {
        for (cipher_suite, profile, algos, curves) in [
            (CipherSuite::Cv25519,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::EdDSA, PublicKeyAlgorithm::ECDH ][..],
             &[ Curve::Ed25519, Curve::Cv25519, ][..]),
            (CipherSuite::Cv25519,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::Ed25519, PublicKeyAlgorithm::X25519 ],
             &[]),

            (CipherSuite::Cv448,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::Ed448, PublicKeyAlgorithm::X448 ],
             &[]),
            (CipherSuite::Cv448,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::Ed448, PublicKeyAlgorithm::X448 ],
             &[]),

            (CipherSuite::RSA2k,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),
            (CipherSuite::RSA2k,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),

            (CipherSuite::RSA3k,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),
            (CipherSuite::RSA3k,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),

            (CipherSuite::RSA4k,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),
            (CipherSuite::RSA4k,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::RSAEncryptSign ],
             &[]),

            (CipherSuite::P256,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP256 ]),
            (CipherSuite::P256,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP256 ]),

            (CipherSuite::P384,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP384 ]),
            (CipherSuite::P384,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP384 ]),

            (CipherSuite::P521,
             Profile::RFC4880,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP521 ]),
            (CipherSuite::P521,
             Profile::RFC9580,
             &[ PublicKeyAlgorithm::ECDSA, PublicKeyAlgorithm::ECDH ],
             &[ Curve::NistP521 ]),
        ]
        {
            eprintln!("Testing that generating {:?}, {:?} results in \
                       algorithms: {}; curves: {}",
                      cipher_suite, profile,
                      algos
                          .iter()
                          .map(|a| a.to_string())
                          .collect::<Vec<_>>()
                          .join(", "),
                      if curves.is_empty() {
                          "none".to_string()
                      } else {
                          curves
                              .iter()
                              .map(|a| a.to_string())
                              .collect::<Vec<_>>()
                              .join(", ")
                      });

            if let Err(err) = cipher_suite.is_supported() {
                eprintln!("Skipping, cipher suite is not supported: {}", err);
                continue;
            }

            let (cert, _) = CertBuilder::general_purpose(Some("x@example.org"))
                .set_cipher_suite(cipher_suite)
                .set_profile(profile)
                .expect("Profile is supported")
                .generate()
                .expect("Cipher suite is supported");

            let mut algos_got = cert.keys()
                .map(|ka| ka.key().pk_algo())
                .collect::<Vec<_>>();
            algos_got.sort();
            algos_got.dedup();

            let mut algos_expected = algos.to_vec();
            algos_expected.sort();

            assert_eq!(&algos_expected, &algos_got,
                       "\n\
                        algos expected: {}\n\
                        algos got:      {}",
                       algos_expected
                           .iter()
                           .map(|a| a.to_string())
                           .collect::<Vec<_>>()
                           .join(", "),
                       algos_got
                           .iter()
                           .map(|a| a.to_string())
                           .collect::<Vec<_>>()
                           .join(", "));


            let mut curves_got = cert.keys()
                .filter_map(|ka| match ka.key().mpis() {
                    PublicKey::EdDSA { curve, .. }
                    | PublicKey::ECDSA { curve, .. }
                    | PublicKey::ECDH { curve, .. } =>
                    {
                        Some(curve.clone())
                    }
                    _ => None,
                })
                .collect::<Vec<_>>();
            curves_got.sort();
            curves_got.dedup();

            let mut curves_expected = curves.to_vec();
            curves_expected.sort();

            assert_eq!(&curves_expected, &curves_got,
                       "\n\
                        curves expected: {}\n\
                        curves got:      {}",
                       if curves_expected.is_empty() {
                           "(none)".to_string()
                       } else {
                           curves_expected
                               .iter()
                               .map(|a| a.to_string())
                               .collect::<Vec<_>>()
                               .join(", ")
                       },
                       if curves_got.is_empty() {
                           "(none)".to_string()
                       } else {
                           curves_got
                               .iter()
                               .map(|a| a.to_string())
                               .collect::<Vec<_>>()
                               .join(", ")
                       });
        }
    }
}
