From 51e75567206d9645a36b2d1df5fac1dee1279129 Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Tue, 23 Jan 2024 09:10:22 +0100 Subject: openpgp: Add Cert::exportable. - Add `Cert::exportable` to return whether a certificate is exportable or not. - A certificate should only be exported if it has at least one exportable direct key signature, or there is at least one user ID or user attribute with at least one exportable self-signature. --- openpgp/NEWS | 1 + openpgp/src/serialize/cert.rs | 59 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/openpgp/NEWS b/openpgp/NEWS index 1ce386c5..9b8204f4 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 + - Cert::exportable - CertBuilder::set_exportable * Changes in 1.17.0 ** Notable fixes diff --git a/openpgp/src/serialize/cert.rs b/openpgp/src/serialize/cert.rs index 764a16ac..3278ea2b 100644 --- a/openpgp/src/serialize/cert.rs +++ b/openpgp/src/serialize/cert.rs @@ -11,6 +11,65 @@ use crate::serialize::{ impl Cert { + /// Returns whether the certificate should be exported. + /// + /// A certificate should only be exported if it has at least one + /// exportable direct key signature, or there is at least one user + /// ID with at least one exportable self signature. + /// + /// # Examples + /// + /// ``` + /// use sequoia_openpgp as openpgp; + /// use openpgp::cert::prelude::*; + /// + /// # fn main() -> openpgp::Result<()> { + /// // By default, certificates are exportable. + /// let (cert, _) = + /// CertBuilder::general_purpose(None, Some("alice@example.org")) + /// .generate()?; + /// assert!(cert.exportable()); + /// + /// // Setting the exportable flag to false makes them + /// // not-exportable. + /// let (cert, _) = + /// CertBuilder::general_purpose(None, Some("alice@example.org")) + /// .set_exportable(false) + /// .generate()?; + /// assert!(! cert.exportable()); + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::if_same_then_else)] + pub fn exportable(&self) -> bool { + let pk = self.primary_key(); + + if pk.self_signatures().chain(pk.self_revocations()) + .any(|sig| sig.exportable().is_ok()) + { + // Exportable direct key signature. Export it. + true + } else if self.userids().any(|userid| { + userid.self_signatures() + .chain(userid.self_revocations()) + .any(|sig| sig.exportable().is_ok()) + }) { + // User ID with exportable self signature. Export it. + true + } else if self.user_attributes().any(|ua| { + ua.self_signatures() + .chain(ua.self_revocations()) + .any(|sig| sig.exportable().is_ok()) + }) { + // User attribute with exportable self signature. Export + // it. + true + } else { + // Don't export it. + false + } + } + /// Serializes or exports the Cert. /// /// If `export` is true, then non-exportable signatures are not -- cgit v1.2.3