diff options
Diffstat (limited to 'openpgp/src/cert/builder.rs')
-rw-r--r-- | openpgp/src/cert/builder.rs | 829 |
1 files changed, 779 insertions, 50 deletions
diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index aac45a50..4243cb2f 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -18,9 +18,35 @@ use crate::types::{ KeyFlags, SignatureType, SymmetricAlgorithm, + RevocationKey, }; -/// Groups symmetric and asymmetric algorithms +/// 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(None, Some("alice@example.org")) +/// .set_cipher_suite(CipherSuite::Cv25519) +/// .generate()?; +/// assert_eq!(ecc.primary_key().pk_algo(), PublicKeyAlgorithm::EdDSA); +/// +/// let (rsa, _) = +/// CertBuilder::general_purpose(None, Some("alice@example.org")) +/// .set_cipher_suite(CipherSuite::RSA4k) +/// .generate()?; +/// assert_eq!(rsa.primary_key().pk_algo(), PublicKeyAlgorithm::RSAEncryptSign); +/// # Ok(()) +/// # } +/// ``` #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)] pub enum CipherSuite { /// EdDSA and ECDH over Curve25519 with SHA512 and AES256 @@ -105,9 +131,34 @@ pub struct KeyBlueprint { ciphersuite: Option<CipherSuite>, } -/// Simplifies generation of Keys. +/// 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]: ../packet/struct.UserID.html +/// [`UserAttribute`s]: ../packet/user_attribute/struct.UserAttribute.html +/// [`Key`s]: ../packet/key/enum.Key.html +/// +/// # Examples +/// +/// Generate a general-purpose certificate with one User ID: /// -/// Builder to generate complex Cert hierarchies with multiple user IDs. +/// ``` +/// use sequoia_openpgp as openpgp; +/// use openpgp::cert::prelude::*; +/// +/// # fn main() -> openpgp::Result<()> { +/// let (cert, rev) = +/// CertBuilder::general_purpose(None, Some("alice@example.org")) +/// .generate()?; +/// # Ok(()) +/// # } +/// ``` #[derive(Clone, Debug)] pub struct CertBuilder { creation_time: Option<std::time::SystemTime>, @@ -117,19 +168,46 @@ pub struct CertBuilder { userids: Vec<packet::UserID>, user_attributes: Vec<packet::UserAttribute>, password: Option<Password>, + revocation_keys: Option<Vec<RevocationKey>>, } 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_transport_encryption_subkey`, for instance), and user - /// ids (using `CertBuilder::add_userid`). + /// 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.). + /// + /// [`CertBuilder::add_signing_subkey`]: #method.add_signing_subkey + /// [`CertBuilder::add_transport_encryption_subkey`]: #method.add_transport_encryption_subkey + /// [`CertBuilder::add_userid`]: #method.add_userid + /// + /// # 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{ + CertBuilder { creation_time: None, ciphersuite: CipherSuite::default(), primary: KeyBlueprint{ @@ -141,13 +219,33 @@ impl CertBuilder { userids: vec![], user_attributes: vec![], password: None, + revocation_keys: None, } } - /// Generates a general-purpose key. + /// Generates a general-purpose certificate. + /// + /// The returned builder is set to generate a certificate with a + /// certification- and signature-capable primary key, and an + /// encryption-capable subkey. The subkey is marked as being + /// appropriate for both data in transit and data at rest. + /// + /// # Examples + /// + /// ``` + /// use sequoia_openpgp as openpgp; + /// use openpgp::cert::prelude::*; /// - /// The key's primary key is certification- and signature-capable. - /// The key has one subkey, an encryption-capable subkey. + /// # fn main() -> openpgp::Result<()> { + /// let (cert, rev) = + /// CertBuilder::general_purpose(None, + /// Some("Alice Lovelace <alice@example.org>")) + /// .generate()?; + /// # assert_eq!(cert.keys().count(), 2); + /// # assert_eq!(cert.userids().count(), 1); + /// # Ok(()) + /// # } + /// ``` pub fn general_purpose<C, U>(ciphersuite: C, userids: Option<U>) -> Self where C: Into<Option<CipherSuite>>, U: Into<packet::UserID> @@ -176,13 +274,55 @@ impl CertBuilder { userids: userids.into_iter().map(|x| x.into()).collect(), user_attributes: vec![], password: None, + revocation_keys: None, } } /// Sets the creation time. /// - /// If `creation_time` is `None`, this causes the `Builder` to use - /// the time when `Builder::generate` is called. + /// If `creation_time` is `None`, the default, this causes the + /// `CertBuilder` to use that time when [`CertBuilder::generate`] + /// is called. + /// + /// 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`]: #method.generate + /// [`SystemTime`]: https://doc.rust-lang.org/stable/std/time/struct.SystemTime.html + /// [`Timestamp`]: ../types/struct.Timestamp.html + /// [`generate`]: #method.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(None, + /// Some("Alice Lovelace <alice@example.org>")) + /// .set_creation_time(t) + /// .generate()?; + /// assert_eq!(cert.primary_key().self_signatures()[0].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>>, { @@ -190,13 +330,80 @@ impl CertBuilder { self } - /// Sets the encryption and signature algorithms for primary and all subkeys. + /// 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(None, Some("alice@example.org")) + /// .set_cipher_suite(CipherSuite::Cv25519) + /// .generate()?; + /// assert_eq!(ecc.primary_key().pk_algo(), PublicKeyAlgorithm::EdDSA); + /// + /// let (rsa, _) = + /// CertBuilder::general_purpose(None, Some("alice@example.org")) + /// .set_cipher_suite(CipherSuite::RSA4k) + /// .generate()?; + /// assert_eq!(rsa.primary_key().pk_algo(), PublicKeyAlgorithm::RSAEncryptSign); + /// # Ok(()) + /// # } + /// ``` 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. + /// 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`]: #method.general_purpose + /// [primary User ID flag]: https://tools.ietf.org/html/rfc4880#section-5.2.3.19 + /// + /// # 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(None, + /// 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.value().cmp(b.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<'a, U>(mut self, uid: U) -> Self where U: Into<packet::UserID> { @@ -204,7 +411,79 @@ impl CertBuilder { self } - /// Adds a new user attribute. + /// 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://tools.ietf.org/html/rfc4880#section-5.2.3.19 + /// + /// # 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<'a, U>(mut self, ua: U) -> Self where U: Into<packet::UserAttribute> { @@ -212,29 +491,193 @@ impl CertBuilder { self } - /// Adds a signing capable subkey. + /// 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`]: #method.set_cipher_suite + /// [`CertBuilder::add_subkey`]: #method.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(true))); + /// # Ok(()) + /// # } + /// ``` pub fn add_signing_subkey(self) -> Self { self.add_subkey(KeyFlags::default().set_signing(true), 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`]: #method.set_cipher_suite + /// [`CertBuilder::add_subkey`]: #method.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(true))); + /// # Ok(()) + /// # } + /// ``` pub fn add_transport_encryption_subkey(self) -> Self { self.add_subkey(KeyFlags::default().set_transport_encryption(true), 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`]: #method.set_cipher_suite + /// [`CertBuilder::add_subkey`]: #method.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(true))); + /// # Ok(()) + /// # } + /// ``` pub fn add_storage_encryption_subkey(self) -> Self { self.add_subkey(KeyFlags::default().set_storage_encryption(true), None, None) } - /// Adds an certification capable subkey. + /// Adds an 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`]: #method.set_cipher_suite + /// [`CertBuilder::add_subkey`]: #method.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(true))); + /// # Ok(()) + /// # } + /// ``` pub fn add_certification_subkey(self) -> Self { self.add_subkey(KeyFlags::default().set_certification(true), None, None) } - /// Adds an authentication capable subkey. + /// 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`]: #method.set_cipher_suite + /// [`CertBuilder::add_subkey`]: #method.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(true))); + /// # Ok(()) + /// # } + /// ``` pub fn add_authentication_subkey(self) -> Self { self.add_subkey(KeyFlags::default().set_authentication(true), None, None) } @@ -243,35 +686,160 @@ impl CertBuilder { /// /// If `expiration` is `None`, the subkey uses the same expiration /// time 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_expiration_time(now + 2 * y) + /// .add_subkey(KeyFlags::empty() + /// .set_storage_encryption(true) + /// .set_transport_encryption(true), + /// now + 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(true) + /// .set_transport_encryption(true))); + /// # Ok(()) } + /// ``` pub fn add_subkey<T, C>(mut self, flags: KeyFlags, expiration: T, cs: C) -> Self where T: Into<Option<time::SystemTime>>, C: Into<Option<CipherSuite>>, { self.subkeys.push(KeyBlueprint { - flags: flags, + flags, expiration: expiration.into(), ciphersuite: cs.into(), }); self } - /// Sets the capabilities of the primary key. The function automatically - /// makes the primary key certification capable if subkeys are added. + /// 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 + /// + /// Make the primary key certification and signing 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(None, + /// Some("Alice Lovelace <alice@example.org>")) + /// .set_primary_key_flags(KeyFlags::empty().set_signing(true)) + /// .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(true).set_certification(true))); + /// # 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(None, + /// Some("Alice Lovelace <alice@example.org>")) + /// .set_password(Some("1234".into())) + /// .generate()?; + /// + /// for ka in cert.keys() { + /// assert!(ka.has_secret()); + /// } + /// # Ok(()) } + /// ``` pub fn set_password(mut self, password: Option<Password>) -> Self { self.password = password; self } - /// Sets the expiration time. + /// Sets the certificate's expiration time. /// /// A value of None means never. + // + /// # 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_expiration_time(now + 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_expiration_time<T>(mut self, expiration: T) -> Self where T: Into<Option<time::SystemTime>> { @@ -279,10 +847,69 @@ impl CertBuilder { self } - /// Generates the actual Cert. + /// 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(None, Some("alice@example.org")) + /// .generate()?; + /// let (bob, _) = + /// CertBuilder::general_purpose(None, 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 + /// + /// ``` + / |