summaryrefslogtreecommitdiffstats
path: root/openpgp/src/cert
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/cert')
-rw-r--r--openpgp/src/cert/bindings.rs365
-rw-r--r--openpgp/src/cert/builder.rs665
-rw-r--r--openpgp/src/cert/keyiter.rs501
-rw-r--r--openpgp/src/cert/mod.rs3115
-rw-r--r--openpgp/src/cert/parser/low_level/grammar.lalrpop297
-rw-r--r--openpgp/src/cert/parser/low_level/grammar.rs2
-rw-r--r--openpgp/src/cert/parser/low_level/lexer.rs156
-rw-r--r--openpgp/src/cert/parser/low_level/mod.rs80
-rw-r--r--openpgp/src/cert/parser/mod.rs931
-rw-r--r--openpgp/src/cert/revoke.rs485
10 files changed, 6597 insertions, 0 deletions
diff --git a/openpgp/src/cert/bindings.rs b/openpgp/src/cert/bindings.rs
new file mode 100644
index 00000000..032c7ba2
--- /dev/null
+++ b/openpgp/src/cert/bindings.rs
@@ -0,0 +1,365 @@
+use std::time;
+
+use crate::Error;
+use crate::Result;
+use crate::Cert;
+use crate::types::{HashAlgorithm, SignatureType};
+use crate::conversions::Time;
+use crate::crypto::Signer;
+use crate::packet::{UserID, UserAttribute, key, Key, signature, Signature};
+
+impl<P: key::KeyParts> Key<P, key::SubordinateRole> {
+ /// Creates a binding signature.
+ ///
+ /// The signature binds this userid to `cert`. `signer` will be used
+ /// to create a signature using `signature` as builder.
+ /// The`hash_algo` defaults to SHA512, `creation_time` to the
+ /// current time.
+ ///
+ /// This function adds a creation time subpacket, a issuer
+ /// fingerprint subpacket, and a issuer subpacket to the
+ /// signature.
+ ///
+ /// # Example
+ ///
+ /// This example demonstrates how to bind this key to a Cert. Note
+ /// that in general, the `CertBuilder` is a better way to add
+ /// subkeys to a Cert.
+ ///
+ /// ```
+ /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// // Generate a Cert, and create a keypair from the primary key.
+ /// let (cert, _) = CertBuilder::new().generate()?;
+ /// let mut keypair = cert.primary().clone()
+ /// .mark_parts_secret()?.into_keypair()?;
+ ///
+ /// // Let's add an encryption subkey.
+ /// let flags = KeyFlags::default().set_encrypt_at_rest(true);
+ /// assert_eq!(cert.keys_valid().key_flags(flags.clone()).count(), 0);
+ ///
+ /// // Generate a subkey and a binding signature.
+ /// let subkey : key::SecretSubkey
+ /// = Key4::generate_ecc(false, Curve::Cv25519)?.into();
+ /// let builder = signature::Builder::new(SignatureType::SubkeyBinding)
+ /// .set_key_flags(&flags)?;
+ /// let binding = subkey.bind(&mut keypair, &cert, builder, None)?;
+ ///
+ /// // Now merge the key and binding signature into the Cert.
+ /// let cert = cert.merge_packets(vec![subkey.into(),
+ /// binding.into()])?;
+ ///
+ /// // Check that we have an encryption subkey.
+ /// assert_eq!(cert.keys_valid().key_flags(flags).count(), 1);
+ /// # Ok(()) }
+ pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert,
+ signature: signature::Builder,
+ creation_time: T)
+ -> Result<Signature>
+ where T: Into<Option<time::SystemTime>>,
+ R: key::KeyRole
+ {
+ signature
+ .set_signature_creation_time(
+ creation_time.into().unwrap_or_else(|| {
+ time::SystemTime::now().canonicalize()
+ }))?
+ .set_issuer_fingerprint(signer.public().fingerprint())?
+ .set_issuer(signer.public().keyid())?
+ .sign_subkey_binding(signer, cert.primary(), self)
+ }
+}
+
+impl UserID {
+ /// Creates a binding signature.
+ ///
+ /// The signature binds this userid to `cert`. `signer` will be used
+ /// to create a signature using `signature` as builder.
+ /// The`hash_algo` defaults to SHA512, `creation_time` to the
+ /// current time.
+ ///
+ /// This function adds a creation time subpacket, a issuer
+ /// fingerprint subpacket, and a issuer subpacket to the
+ /// signature.
+ ///
+ /// # Example
+ ///
+ /// This example demonstrates how to bind this userid to a Cert.
+ /// Note that in general, the `CertBuilder` is a better way to add
+ /// userids to a Cert.
+ ///
+ /// ```
+ /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// // Generate a Cert, and create a keypair from the primary key.
+ /// let (cert, _) = CertBuilder::new().generate()?;
+ /// let mut keypair = cert.primary().clone()
+ /// .mark_parts_secret()?.into_keypair()?;
+ /// assert_eq!(cert.userids().len(), 0);
+ ///
+ /// // Generate a userid and a binding signature.
+ /// let userid = UserID::from("test@example.org");
+ /// let builder =
+ /// signature::Builder::new(SignatureType::PositiveCertificate);
+ /// let binding = userid.bind(&mut keypair, &cert, builder, None)?;
+ ///
+ /// // Now merge the userid and binding signature into the Cert.
+ /// let cert = cert.merge_packets(vec![userid.into(), binding.into()])?;
+ ///
+ /// // Check that we have a userid.
+ /// assert_eq!(cert.userids().len(), 1);
+ /// # Ok(()) }
+ pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert,
+ signature: signature::Builder,
+ creation_time: T)
+ -> Result<Signature>
+ where T: Into<Option<time::SystemTime>>,
+ R: key::KeyRole
+ {
+ signature
+ .set_signature_creation_time(
+ creation_time.into().unwrap_or_else(|| {
+ time::SystemTime::now().canonicalize()
+ }))?
+ .set_issuer_fingerprint(signer.public().fingerprint())?
+ .set_issuer(signer.public().keyid())?
+ .sign_userid_binding(
+ signer, cert.primary(), self)
+ }
+
+ /// Returns a certificate for the user id.
+ ///
+ /// The signature binds this userid to `cert`. `signer` will be
+ /// used to create a certification signature of type
+ /// `signature_type`. `signature_type` defaults to
+ /// `SignatureType::GenericCertificate`, `hash_algo` to SHA512,
+ /// `creation_time` to the current time.
+ ///
+ /// This function adds a creation time subpacket, a issuer
+ /// fingerprint subpacket, and a issuer subpacket to the
+ /// signature.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Error::InvalidArgument` if `signature_type` is not
+ /// one of `SignatureType::{Generic, Persona, Casual,
+ /// Positive}Certificate`
+ ///
+ /// # Example
+ ///
+ /// This example demonstrates how to certify a userid.
+ ///
+ /// ```
+ /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// // Generate a Cert, and create a keypair from the primary key.
+ /// let (alice, _) = CertBuilder::new()
+ /// .primary_keyflags(KeyFlags::default().set_certify(true))
+ /// .add_userid("alice@example.org")
+ /// .generate()?;
+ /// let mut keypair = alice.primary().clone()
+ /// .mark_parts_secret()?.into_keypair()?;
+ ///
+ /// // Generate a Cert for Bob.
+ /// let (bob, _) = CertBuilder::new()
+ /// .primary_keyflags(KeyFlags::default().set_certify(true))
+ /// .add_userid("bob@example.org")
+ /// .generate()?;
+ ///
+ /// // Alice now certifies the binding between `bob@example.org` and `bob`.
+ /// let certificate =
+ /// bob.userids().nth(0).unwrap().userid()
+ /// .certify(&mut keypair, &bob, SignatureType::PositiveCertificate,
+ /// None, None)?;
+ ///
+ /// // `certificate` can now be used, e.g. by merging it into `bob`.
+ /// let bob = bob.merge_packets(vec![certificate.into()])?;
+ ///
+ /// // Check that we have a certification on the userid.
+ /// assert_eq!(bob.userids().nth(0).unwrap().certifications().len(), 1);
+ /// # Ok(()) }
+ pub fn certify<S, H, T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert,
+ signature_type: S,
+ hash_algo: H, creation_time: T)
+ -> Result<Signature>
+ where S: Into<Option<SignatureType>>,
+ H: Into<Option<HashAlgorithm>>,
+ T: Into<Option<time::SystemTime>>,
+ R: key::KeyRole
+ {
+ let typ = signature_type.into();
+ let typ = match typ {
+ Some(SignatureType::GenericCertificate)
+ | Some(SignatureType::PersonaCertificate)
+ | Some(SignatureType::CasualCertificate)
+ | Some(SignatureType::PositiveCertificate) => typ.unwrap(),
+ Some(t) => return Err(Error::InvalidArgument(
+ format!("Invalid signature type: {}", t)).into()),
+ None => SignatureType::GenericCertificate,
+ };
+ let mut sig = signature::Builder::new(typ);
+ if let Some(algo) = hash_algo.into() {
+ sig = sig.set_hash_algo(algo);
+ }
+ self.bind(signer, cert, sig,
+ // Unwrap arguments to prevent further
+ // monomorphization of bind().
+ creation_time.into().unwrap_or_else(|| {
+ time::SystemTime::now().canonicalize()
+ }))
+ }
+}
+
+impl UserAttribute {
+ /// Creates a binding signature.
+ ///
+ /// The signature binds this user attribute to `cert`. `signer`
+ /// will be used to create a signature using `signature` as
+ /// builder. The`hash_algo` defaults to SHA512, `creation_time`
+ /// to the current time.
+ ///
+ /// This function adds a creation time subpacket, a issuer
+ /// fingerprint subpacket, and a issuer subpacket to the
+ /// signature.
+ ///
+ /// # Example
+ ///
+ /// This example demonstrates how to bind this user attribute to a
+ /// Cert. Note that in general, the `CertBuilder` is a better way
+ /// to add userids to a Cert.
+ ///
+ /// ```
+ /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*,
+ /// packet::user_attribute::*};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// // Generate a Cert, and create a keypair from the primary key.
+ /// let (cert, _) = CertBuilder::new()
+ /// .generate()?;
+ /// let mut keypair = cert.primary().clone()
+ /// .mark_parts_secret()?.into_keypair()?;
+ /// assert_eq!(cert.userids().len(), 0);
+ ///
+ /// // Generate a user attribute and a binding signature.
+ /// let user_attr = UserAttribute::new(&[
+ /// Subpacket::Image(
+ /// Image::Private(100, vec![0, 1, 2].into_boxed_slice())),
+ /// ])?;
+ /// let builder =
+ /// signature::Builder::new(SignatureType::PositiveCertificate);
+ /// let binding = user_attr.bind(&mut keypair, &cert, builder, None)?;
+ ///
+ /// // Now merge the user attribute and binding signature into the Cert.
+ /// let cert = cert.merge_packets(vec![user_attr.into(), binding.into()])?;
+ ///
+ /// // Check that we have a user attribute.
+ /// assert_eq!(cert.user_attributes().len(), 1);
+ /// # Ok(()) }
+ pub fn bind<T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert,
+ signature: signature::Builder,
+ creation_time: T)
+ -> Result<Signature>
+ where T: Into<Option<time::SystemTime>>,
+ R: key::KeyRole
+ {
+ signature
+ .set_signature_creation_time(
+ creation_time.into().unwrap_or_else(|| {
+ time::SystemTime::now().canonicalize()
+ }))?
+ .set_issuer_fingerprint(signer.public().fingerprint())?
+ .set_issuer(signer.public().keyid())?
+ .sign_user_attribute_binding(signer, cert.primary(), self)
+ }
+
+ /// Returns a certificate for the user attribute.
+ ///
+ /// The signature binds this user attribute to `cert`. `signer` will be
+ /// used to create a certification signature of type
+ /// `signature_type`. `signature_type` defaults to
+ /// `SignatureType::GenericCertificate`, `hash_algo` to SHA512,
+ /// `creation_time` to the current time.
+ ///
+ /// This function adds a creation time subpacket, a issuer
+ /// fingerprint subpacket, and a issuer subpacket to the
+ /// signature.
+ ///
+ /// # Errors
+ ///
+ /// Returns `Error::InvalidArgument` if `signature_type` is not
+ /// one of `SignatureType::{Generic, Persona, Casual,
+ /// Positive}Certificate`
+ ///
+ /// # Example
+ ///
+ /// This example demonstrates how to certify a userid.
+ ///
+ /// ```
+ /// # use sequoia_openpgp::{*, packet::prelude::*, types::*, cert::*,
+ /// packet::user_attribute::*};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// // Generate a Cert, and create a keypair from the primary key.
+ /// let (alice, _) = CertBuilder::new()
+ /// .add_userid("alice@example.org")
+ /// .generate()?;
+ /// let mut keypair = alice.primary().clone()
+ /// .mark_parts_secret()?.into_keypair()?;
+ ///
+ /// // Generate a Cert for Bob.
+ /// let user_attr = UserAttribute::new(&[
+ /// Subpacket::Image(
+ /// Image::Private(100, vec![0, 1, 2].into_boxed_slice())),
+ /// ])?;
+ /// let (bob, _) = CertBuilder::new()
+ /// .primary_keyflags(KeyFlags::default().set_certify(true))
+ /// .add_user_attribute(user_attr)
+ /// .generate()?;
+ ///
+ /// // Alice now certifies the binding between `bob@example.org` and `bob`.
+ /// let certificate =
+ /// bob.user_attributes().nth(0).unwrap().user_attribute()
+ /// .certify(&mut keypair, &bob, SignatureType::PositiveCertificate,
+ /// None, None)?;
+ ///
+ /// // `certificate` can now be used, e.g. by merging it into `bob`.
+ /// let bob = bob.merge_packets(vec![certificate.into()])?;
+ ///
+ /// // Check that we have a certification on the userid.
+ /// assert_eq!(bob.user_attributes().nth(0).unwrap().certifications().len(),
+ /// 1);
+ /// # Ok(()) }
+ pub fn certify<S, H, T, R>(&self, signer: &mut dyn Signer<R>, cert: &Cert,
+ signature_type: S,
+ hash_algo: H, creation_time: T)
+ -> Result<Signature>
+ where S: Into<Option<SignatureType>>,
+ H: Into<Option<HashAlgorithm>>,
+ T: Into<Option<time::SystemTime>>,
+ R: key::KeyRole
+ {
+ let typ = signature_type.into();
+ let typ = match typ {
+ Some(SignatureType::GenericCertificate)
+ | Some(SignatureType::PersonaCertificate)
+ | Some(SignatureType::CasualCertificate)
+ | Some(SignatureType::PositiveCertificate) => typ.unwrap(),
+ Some(t) => return Err(Error::InvalidArgument(
+ format!("Invalid signature type: {}", t)).into()),
+ None => SignatureType::GenericCertificate,
+ };
+ let mut sig = signature::Builder::new(typ);
+ if let Some(algo) = hash_algo.into() {
+ sig = sig.set_hash_algo(algo);
+ }
+ self.bind(signer, cert, sig,
+ // Unwrap arguments to prevent further
+ // monomorphization of bind().
+ creation_time.into().unwrap_or_else(|| {
+ time::SystemTime::now().canonicalize()
+ }))
+ }
+}
diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs
new file mode 100644
index 00000000..fe8b6ccf
--- /dev/null
+++ b/openpgp/src/cert/builder.rs
@@ -0,0 +1,665 @@
+use std::time;
+
+use crate::packet;
+use crate::packet::{
+ key,
+ Key,
+ key::Key4,
+};
+use crate::Result;
+use crate::packet::Signature;
+use crate::packet::signature;
+use crate::Cert;
+use crate::cert::CertRevocationBuilder;
+use crate::Error;
+use crate::conversions::Time;
+use crate::crypto::Password;
+use crate::autocrypt::Autocrypt;
+use crate::types::{
+ Features,
+ HashAlgorithm,
+ KeyFlags,
+ SignatureType,
+ SymmetricAlgorithm,
+};
+
+/// Groups symmetric and asymmetric algorithms
+#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
+pub enum CipherSuite {
+ /// EdDSA and ECDH over Curve25519 with SHA512 and AES256
+ Cv25519,
+ /// 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,
+}
+
+impl Default for CipherSuite {
+ fn default() -> Self {
+ CipherSuite::Cv25519
+ }
+}
+
+impl CipherSuite {
+ fn generate_key<R>(self, flags: &KeyFlags)
+ -> Result<Key<key::SecretParts, R>>
+ where R: key::KeyRole
+ {
+ 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::Cv25519 | CipherSuite::P256 |
+ CipherSuite::P384 | CipherSuite::P521 => {
+ let sign = flags.can_certify() || flags.can_sign()
+ || flags.can_authenticate();
+ let encrypt = flags.can_encrypt_for_transport()
+ || flags.can_encrypt_at_rest();
+ 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()),
+ }
+ },
+ }.map(|key| key.into())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct KeyBlueprint {
+ flags: KeyFlags,
+ expiration: Option<time::Duration>,
+}
+
+/// Simplifies generation of Keys.
+///
+/// Builder to generate complex Cert hierarchies with multiple user IDs.
+#[derive(Clone, Debug)]
+pub struct CertBuilder {
+ ciphersuite: CipherSuite,
+ primary: KeyBlueprint,
+ subkeys: Vec<KeyBlueprint>,
+ userids: Vec<packet::UserID>,
+ user_attributes: Vec<packet::UserAttribute>,
+ password: Option<Password>,
+}
+
+impl CertBuilder {
+ /// Returns a new CertBuilder.
+ ///
+ /// The returned CertBuilder is setup to only create a
+ /// certification-capable primary key using the default cipher
+ /// suite. You'll almost certainly want to add subkeys (using
+ /// `CertBuilder::add_signing_subkey`, or
+ /// `CertBuilder::add_encryption_subkey`, for instance), and user
+ /// ids (using `CertBuilder::add_userid`).
+ pub fn new() -> Self {
+ CertBuilder{
+ ciphersuite: CipherSuite::default(),
+ primary: KeyBlueprint{
+ flags: KeyFlags::default().set_certify(true),
+ expiration: None,
+ },
+ subkeys: vec![],
+ userids: vec![],
+ user_attributes: vec![],
+ password: None,
+ }
+ }
+
+ /// Generates a general-purpose key.
+ ///
+ /// The key's primary key is certification- and signature-capable.
+ /// The key has one subkey, an encryption-capable subkey.
+ pub fn general_purpose<C, U>(ciphersuite: C, userids: Option<U>) -> Self
+ where C: Into<Option<CipherSuite>>,
+ U: Into<packet::UserID>
+ {
+ CertBuilder {
+ ciphersuite: ciphersuite.into().unwrap_or(Default::default()),
+ primary: KeyBlueprint {
+ flags: KeyFlags::default()
+ .set_certify(true)
+ .set_sign(true),
+ expiration: Some(
+ time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0)),
+ },
+ subkeys: vec![
+ KeyBlueprint {
+ flags: KeyFlags::default()
+ .set_encrypt_for_transport(true)
+ .set_encrypt_at_rest(true),
+ expiration: None,
+ }
+ ],
+ userids: userids.into_iter().map(|x| x.into()).collect(),
+ user_attributes: vec![],
+ password: None,
+ }
+ }
+
+ /// Generates a key compliant to
+ /// [Autocrypt](https://autocrypt.org/).
+ ///
+ /// If no version is given the latest one is used.
+ ///
+ /// The autocrypt specification requires a UserID. However,
+ /// because it can be useful to add the UserID later, it is
+ /// permitted to be none.
+ pub fn autocrypt<'a, V, U>(version: V, userid: Option<U>)
+ -> Self
+ where V: Into<Option<Autocrypt>>,
+ U: Into<packet::UserID>
+ {
+ let builder = CertBuilder{
+ ciphersuite: match version.into().unwrap_or(Default::default()) {
+ Autocrypt::V1 => CipherSuite::RSA3k,
+ Autocrypt::V1_1 => CipherSuite::Cv25519,
+ },
+ primary: KeyBlueprint {
+ flags: KeyFlags::default()
+ .set_certify(true)
+ .set_sign(true),
+ expiration: Some(
+ time::Duration::new(3 * 52 * 7 * 24 * 60 * 60, 0)),
+ },
+ subkeys: vec![
+ KeyBlueprint {
+ flags: KeyFlags::default()
+ .set_encrypt_for_transport(true)
+ .set_encrypt_at_rest(true),
+ expiration: None,
+ }
+ ],
+ userids: vec![],
+ user_attributes: vec![],
+ password: None,
+ };
+
+ if let Some(userid) = userid {
+ builder.add_userid(userid.into())
+ } else {
+ builder
+ }
+ }
+
+ /// Sets the encryption and signature algorithms for primary and all subkeys.
+ pub fn set_cipher_suite(mut self, cs: CipherSuite) -> Self {
+ self.ciphersuite = cs;
+ self
+ }
+
+ /// Adds a new user ID. The first user ID added will be the primary user ID.
+ pub fn add_userid<'a, U>(mut self, uid: U) -> Self
+ where U: Into<packet::UserID>
+ {
+ self.userids.push(uid.into());
+ self
+ }
+
+ /// Adds a new user attribute.
+ pub fn add_user_attribute<'a, U>(mut self, ua: U) -> Self
+ where U: Into<packet::UserAttribute>
+ {
+ self.user_attributes.push(ua.into());
+ self
+ }
+
+ /// Adds a signing capable subkey.
+ pub fn add_signing_subkey(self) -> Self {
+ self.add_subkey(KeyFlags::default().set_sign(true), None)
+ }
+
+ /// Adds an encryption capable subkey.
+ pub fn add_encryption_subkey(self) -> Self {
+ self.add_subkey(KeyFlags::default()
+ .set_encrypt_for_transport(true)
+ .set_encrypt_at_rest(true), None)
+ }
+
+ /// Adds an certification capable subkey.
+ pub fn add_certification_subkey(self) -> Self {
+ self.add_subkey(KeyFlags::default().set_certify(true), None)
+ }
+
+ /// Adds an authentication capable subkey.
+ pub fn add_authentication_subkey(self) -> Self {
+ self.add_subkey(KeyFlags::default().set_authenticate(true), None)
+ }
+
+ /// Adds a custom subkey.
+ ///
+ /// If `expiration` is `None`, the subkey uses the same expiration
+ /// time as the primary key.
+ pub fn add_subkey<T>(mut self, flags: KeyFlags, expiration: T) -> Self
+ where T: Into<Option<time::Duration>>
+ {
+ self.subkeys.push(KeyBlueprint {
+ flags: flags,
+ expiration: expiration.into(),
+ });
+ self
+ }
+
+ /// Sets the capabilities of the primary key. The function automatically
+ /// makes the primary key certification capable if subkeys are added.
+ pub fn primary_keyflags(mut self, flags: KeyFlags) -> Self {
+ self.primary.flags = flags;
+ self
+ }
+
+ /// Sets a password to encrypt the secret keys with.
+ pub fn set_password(mut self, password: Option<Password>) -> Self {
+ self.password = password;
+ self
+ }
+
+ /// Sets the expiration time.
+ ///
+ /// A value of None means never.
+ pub fn set_expiration<T>(mut self, expiration: T) -> Self
+ where T: Into<Option<time::Duration>>
+ {
+ self.primary.expiration = expiration.into();
+ self
+ }
+
+ /// Generates the actual Cert.
+ pub fn generate(mut self) -> Result<(Cert, Signature)> {
+ use crate::{PacketPile, Packet};
+ use crate::types::ReasonForRevocation;
+
+ let mut packets = Vec::<Packet>::with_capacity(
+ 1 + 1 + self.subkeys.len() + self.userids.len()
+ + self.user_attributes.len());
+
+ // make sure the primary key can sign subkeys
+ if !self.subkeys.is_empty() {
+ self.primary.flags = self.primary.flags.set_certify(true);
+ }
+
+ // Generate & and self-sign primary key.
+ let (primary, sig) = self.primary_key()?;
+ let mut signer = primary.clone().mark_parts_secret().unwrap()
+ .into_keypair().unwrap();
+
+ packets.push(Packet::PublicKey({
+ let mut primary = primary.clone();
+ if let Some(ref password) = self.password {
+ primary.secret_mut().unwrap().encrypt_in_place(password)?;
+ }
+ primary
+ }));
+ packets.push(sig.clone().into());
+
+ let mut cert =
+ Cer