From 7c5efa151eb99429401f871792090c0c5cf9f63e Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Tue, 23 Jan 2024 09:08:04 +0100 Subject: openpgp: Add a method to CertBuilder to make non-exportable certs. - Add `CertBuilder::set_exportable`, which sets the exportable flag accordingly on all generated signatures. - This allows the easy creation of non-exportable certificates, which is recommended by the OpenPGP Certificate Directory specification for the local trust root: > The trust root is an OpenPGP certificate that is stored under > the special name trust-root. > > The certificate: > > SHOULD use direct key signatures or binding signatures that are marked as non-exportable. https://www.ietf.org/archive/id/draft-nwjw-openpgp-cert-d-00.html#section-3.5.1 - Fixes #1082. --- openpgp/NEWS | 1 + openpgp/src/cert/builder.rs | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'openpgp') diff --git a/openpgp/NEWS b/openpgp/NEWS index 1aac12d3..1ce386c5 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -14,6 +14,7 @@ - KeyAmalgamation::valid_third_party_revocations_by_key - Parse::from_buffered_reader - armor::Reader::from_buffered_reader + - CertBuilder::set_exportable * Changes in 1.17.0 ** Notable fixes - Sequoia now ignores some formatting errors when reading secret diff --git a/openpgp/src/cert/builder.rs b/openpgp/src/cert/builder.rs index 82045295..db864ed0 100644 --- a/openpgp/src/cert/builder.rs +++ b/openpgp/src/cert/builder.rs @@ -275,6 +275,7 @@ pub struct CertBuilder<'a> { user_attributes: Vec<(Option, packet::UserAttribute)>, password: Option, revocation_keys: Option>, + exportable: bool, phantom: PhantomData<&'a ()>, } assert_send_and_sync!(CertBuilder<'_>); @@ -331,6 +332,7 @@ impl CertBuilder<'_> { user_attributes: vec![], password: None, revocation_keys: None, + exportable: true, phantom: PhantomData, } } @@ -507,6 +509,18 @@ impl CertBuilder<'_> { 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 of the signatures that it + /// creates include the an [Exportable Certification] subpacket + /// that is set to `false`. + pub fn set_exportable(mut self, exportable: bool) -> Self { + self.exportable = exportable; + self + } + /// Adds a User ID. /// /// Adds a User ID to the certificate. The first User ID that is @@ -1387,7 +1401,8 @@ impl CertBuilder<'_> { for (template, uid) in self.userids.into_iter() { let sig = template.unwrap_or_else( || SignatureBuilder::new(SignatureType::PositiveCertification)); - let sig = Self::signature_common(sig, creation_time)?; + let sig = Self::signature_common(sig, creation_time, + self.exportable)?; let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?; // Make sure we mark exactly one User ID or Attribute as @@ -1417,7 +1432,8 @@ impl CertBuilder<'_> { for (template, ua) in self.user_attributes.into_iter() { let sig = template.unwrap_or_else( || SignatureBuilder::new(SignatureType::PositiveCertification)); - let sig = Self::signature_common(sig, creation_time)?; + let sig = Self::signature_common( + sig, creation_time, self.exportable)?; let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?; // Make sure we mark exactly one User ID or Attribute as @@ -1453,7 +1469,8 @@ impl CertBuilder<'_> { let sig = template.unwrap_or_else( || SignatureBuilder::new(SignatureType::SubkeyBinding)); - let sig = Self::signature_common(sig, creation_time)?; + 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))?; @@ -1507,7 +1524,8 @@ impl CertBuilder<'_> { .generate_key(KeyFlags::empty().set_certification())?; key.set_creation_time(creation_time)?; let sig = SignatureBuilder::new(SignatureType::DirectKey); - let sig = Self::signature_common(sig, creation_time)?; + let sig = Self::signature_common( + sig, creation_time, self.exportable)?; let mut sig = Self::add_primary_key_metadata(sig, &self.primary)?; if let Some(ref revocation_keys) = self.revocation_keys { @@ -1523,13 +1541,18 @@ impl CertBuilder<'_> { /// Common settings for generated signatures. fn signature_common(builder: SignatureBuilder, - creation_time: time::SystemTime) + creation_time: time::SystemTime, + exportable: bool) -> Result { - builder + 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) + .set_signature_creation_time(creation_time)?; + if ! exportable { + builder = builder.set_exportable_certification(false)?; + } + Ok(builder) } -- cgit v1.2.3