//! A certificate component and its associated signatures. //! //! Certificates ([`Cert`]s) are a collection of components where each //! component corresponds to a [`Packet`], and each component has zero //! or more associated [`Signature`]s. A [`ComponentBundle`] //! encapsulates a component and its associated signatures. //! //! Sequoia supports four different kinds of components: [`Key`]s, //! [`UserID`]s, [`UserAttribute`]s, and [`Unknown`] components. The //! `Unknown` component has two purposes. First, it is used to store //! packets that appear in a certificate and have an unknown [`Tag`]. //! By not silently dropping these packets, it is possible to round //! trip certificates without losing any information. This provides a //! measure of future compatibility. Second, the `Unknown` component //! is used to store unsupported components. For instance, Sequoia //! doesn't support v3 `Key`s, which are deprecated, or v5 `Key`s, //! which are still being standardized. Because these keys are //! effectively unusable, they are stored as `Unknown` components //! instead of `Key`s. //! //! There are four types of signatures associated with a component: //! self signatures, self revocations, third-party signatures, and //! third-party revocations. When parsing a certificate, self //! signatures and self revocations are checked for validity and //! invalid signatures and revocations are discarded. Since the keys //! are not normally available, third-party signatures and third-party //! revocations cannot be rigorously (i.e., cryptographically) checked //! for validity. //! //! With the exception of the primary key, a component's self //! signatures are binding signatures. A binding signature firstly //! binds the component to the certificate. That is, it provides //! cryptographic evidence that the certificate holder intended for //! the component to be associated with the certificate. Binding //! signatures also provide information about the component. For //! instance, the binding signature for a subkey includes its //! capabilities, and its expiry time. //! //! Since the primary key is the embodiment of the certificate, there //! is nothing to bind it to. Correspondingly, self signatures on a //! primary key are called direct key signatures. Direct key //! signatures are used to provide information about the whole //! certificate. For instance, they can include the default `Key` //! expiry time. This is used if a subkey's binding signature doesn't //! include a expiry. //! //! Self-revocations are revocation certificates issued by the key //! certificate holder. //! //! Third-party signatures are typically signatures certifying that a //! `User ID` or `User Attribute` accurately describes the certificate //! holder. This information is used by trust models, like the Web of //! Trust, to indirectly authenticate keys. //! //! Third-party revocations are revocations issued by another //! certificate. They should normally only be respected if the //! certificate holder made the issuer a so-called [designated //! revoker]. //! //! # Important //! //! When looking up information about a component, it is generally //! better to use the [`ComponentAmalgamation`] or [`KeyAmalgamation`] //! data structures. These data structures provide convenience //! methods that implement the [complicated semantics] for correctly //! locating information. //! //! [`Cert`]: ../index.html //! [`Packet`]: ../../packet/index.html //! [`Signature`]: ../../packet/signature/index.html //! [`ComponentBundle`]: ./struct.ComponentBundle.html //! [`Key`]: ../../packet/key/index.html //! [`UserID`]: ../../packet/struct.UserID.html //! [`UserAttribute`]: ../../packet/user_attribute/index.html //! [`Unknown`]: ../../packet/struct.Unknown.html //! [`Tag`]: ../../packet/enum.Tag.html //! [designated revoker]: https://tools.ietf.org/html/rfc4880#section-5.2.3.15 //! [`ComponentAmalgamation`]: ../amalgamation/index.html //! [`KeyAmalgamation`]: ../key_amalgamation/index.html //! [complicated semantics]: https://tools.ietf.org/html/rfc4880#section-5.2.3.3 use std::time; use std::ops::Deref; use crate::{ Error, packet::Signature, packet::Key, packet::key, packet::UserID, packet::UserAttribute, packet::Unknown, Packet, policy::HashAlgoSecurity, policy::Policy, Result, }; use crate::types::{ RevocationType, RevocationStatus, }; use super::{ sig_cmp, canonical_signature_order, }; /// A certificate component and its associated signatures. /// /// [See the module level documentation](index.html) for a detailed /// description. #[derive(Debug, Clone, PartialEq)] pub struct ComponentBundle { pub(crate) component: C, pub(crate) hash_algo_security: HashAlgoSecurity, // Self signatures. pub(crate) self_signatures: Vec, // Third-party certifications. (In general, this will only be by // designated revokers.) pub(crate) certifications: Vec, // Self revocations. pub(crate) self_revocations: Vec, // Third-party revocations (e.g., designated revokers). pub(crate) other_revocations: Vec, } assert_send_and_sync!(ComponentBundle where C); /// A key (primary or subkey, public or private) and any associated /// signatures. /// /// [See the module level documentation.](index.html) pub type KeyBundle = ComponentBundle>; /// A primary key and any associated signatures. /// /// [See the module level documentation.](index.html) pub type PrimaryKeyBundle = KeyBundle; /// A subkey and any associated signatures. /// /// [See the module level documentation.](index.html) pub type SubkeyBundle = KeyBundle; /// A User ID and any associated signatures. /// /// [See the module level documentation.](index.html) pub type UserIDBundle = ComponentBundle; /// A User Attribute and any associated signatures. /// /// [See the module level documentation.](index.html) pub type UserAttributeBundle = ComponentBundle; /// An unknown component and any associated signatures. /// /// Note: all signatures are stored as certifications. /// /// [See the module level documentation.](index.html) pub type UnknownBundle = ComponentBundle; impl Deref for ComponentBundle { type Target = C; fn deref(&self) -> &Self::Target { &self.component } } impl ComponentBundle { /// Returns a reference to the bundle's component. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// # /// # fn main() -> openpgp::Result<()> { /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display some information about any unknown components. /// for u in cert.unknowns() { /// eprintln!(" - {:?}", u.component()); /// } /// # Ok(()) } /// ``` pub fn component(&self) -> &C { &self.component } /// Returns a mutable reference to the component. fn component_mut(&mut self) -> &mut C { &mut self.component } /// Returns the active binding signature at time `t`. /// /// The active binding signature is the most recent, non-revoked /// self-signature that is valid according to the `policy` and /// alive at time `t` (`creation time <= t`, `t < expiry`). If /// there are multiple such signatures then the signatures are /// ordered by their MPIs interpreted as byte strings. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display information about each User ID's current active /// // binding signature (the `time` parameter is `None`), if any. /// for ua in cert.userids() { /// eprintln!("{:?}", ua.binding_signature(p, None)); /// } /// # Ok(()) } /// ``` pub fn binding_signature(&self, policy: &dyn Policy, t: T) -> Result<&Signature> where T: Into> { let t = t.into().unwrap_or_else(time::SystemTime::now); // Recall: the signatures are sorted by their creation time in // descending order, i.e., newest first. // // We want the newest signature that is older than `t`, or // that has been created at `t`. So, search for `t`. let i = // Usually, the first signature is what we are looking for. // Short circuit the binary search. if Some(t) >= self.self_signatures.get(0) .and_then(|s| s.signature_creation_time()) { 0 } else { match self.self_signatures.binary_search_by( |s| canonical_signature_order( s.signature_creation_time(), Some(t))) { // If there are multiple matches, then we need to search // backwards to find the first one. Consider: // // t: 9 8 8 8 8 7 // i: 0 1 2 3 4 5 // // If we are looking for t == 8, then binary_search could // return index 1, 2, 3 or 4. Ok(mut i) => { while i > 0 && self.self_signatures[i - 1].signature_creation_time() == Some(t) { i -= 1; } i } // There was no match. `i` is where a new element could // be inserted while maintaining the sorted order. // Consider: // // t: 9 8 6 5 // i: 0 1 2 3 // // If we are looing for t == 7, then binary_search will // return i == 2. That's exactly where we should start // looking. Err(i) => i, } }; let mut sig = None; // Prefer the first error, which is the error arising from the // most recent binding signature that wasn't created after // `t`. let mut error = None; 'next_sig: for s in self.self_signatures[i..].iter() { if let Err(e) = s.signature_alive(t, time::Duration::new(0, 0)) { // We know that t >= signature's creation time. So, // it is expired. But an older signature might not // be. So, keep trying. if error.is_none() { error = Some(e); } continue; } if let Err(e) = policy.signature(s, self.hash_algo_security) { if error.is_none() { error = Some(e); } continue; } // The signature is good, but we may still need to verify the // back sig. if s.typ() == crate::types::SignatureType::SubkeyBinding && s.key_flags().map(|kf| kf.for_signing()).unwrap_or(false) { let mut n = 0; let mut one_good_backsig = false; 'next_backsig: for backsig in s.embedded_signatures() { n += 1; if let Err(e) = backsig.signature_alive( t, time::Duration::new(0, 0)) { // The primary key binding signature is not // alive. if error.is_none() { error = Some(e); } continue 'next_backsig; } if let Err(e) = policy .signature(backsig, self.hash_algo_security) { if error.is_none() { error = Some(e); } continue 'next_backsig; } one_good_backsig = true; } if n == 0 { // This shouldn't happen because // Signature::verify_subkey_binding checks for the // primary key binding signature. But, better be // safe. if error.is_none() { error = Some(Error::BadSignature( "Primary key binding signature missing".into()) .into()); } continue 'next_sig; } if ! one_good_backsig { continue 'next_sig; } } sig = Some(s); break; } if let Some(sig) = sig { Ok(sig) } else if let Some(err) = error { Err(err) } else { Err(Error::NoBindingSignature(t).into()) } } /// Returns the component's self-signatures. /// /// The signatures are validated, and they are sorted by their /// creation time, most recent first. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// for (i, ka) in cert.keys().enumerate() { /// eprintln!("Key #{} ({}) has {:?} self signatures", /// i, ka.fingerprint(), /// ka.self_signatures().len()); /// } /// # Ok(()) } /// ``` pub fn self_signatures(&self) -> &[Signature] { &self.self_signatures } /// Returns the component's third-party certifications. /// /// The signatures are *not* validated. They are sorted by their /// creation time, most recent first. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// for ua in cert.userids() { /// eprintln!("User ID {} has {:?} unverified, third-party certifications", /// String::from_utf8_lossy(ua.userid().value()), /// ua.certifications().len()); /// } /// # Ok(()) } /// ``` pub fn certifications(&self) -> &[Signature] { &self.certifications } /// Returns the component's revocations that were issued by the /// certificate holder. /// /// The revocations are validated, and they are sorted by their /// creation time, most recent first. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// for u in cert.userids() { /// eprintln!("User ID {} has {:?} revocation certificates.", /// String::from_utf8_lossy(u.userid().value()), /// u.self_revocations().len()); /// } /// # Ok(()) } /// ``` pub fn self_revocations(&self) -> &[Signature] { &self.self_revocations } /// Returns the component's revocations that were issued by other /// certificates. /// /// The revocations are *not* validated. They are sorted by their /// creation time, most recent first. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// for u in cert.userids() { /// eprintln!("User ID {} has {:?} unverified, third-party revocation certificates.", /// String::from_utf8_lossy(u.userid().value()), /// u.other_revocations().len()); /// } /// # Ok(()) } /// ``` pub fn other_revocations(&self) -> &[Signature] { &self.other_revocations } /// Returns the component's revocation status at time `t`. /// /// A component is considered to be revoked at time `t` if: /// /// - There is a live revocation at time `t` that is newer than /// all live self signatures at time `t`. /// /// - `hard_revocations_are_final` is true, and there is a hard /// revocation (even if it is not yet live at time `t`, and /// even if there is a newer self-signature). /// /// selfsig must be the newest live self signature at time `t`. pub(crate) fn _revocation_status<'a, T>(&'a self, policy: &dyn Policy, t: T, hard_revocations_are_final: bool, selfsig: Option<&Signature>) -> RevocationStatus<'a> where T: Into> { // Fallback time. let time_zero = || time::UNIX_EPOCH; let t = t.into().unwrap_or_else(time::SystemTime::now); let selfsig_creation_time = selfsig.and_then(|s| s.signature_creation_time()) .unwrap_or_else(time_zero); tracer!(super::TRACE, "ComponentBundle::_revocation_status", 0); t!("hard_revocations_are_final: {}, selfsig: {:?}, t: {:?}", hard_revocations_are_final, selfsig_creation_time, t); if let Some(selfsig) = selfsig { assert!( selfsig.signature_alive(t, time::Duration::new(0, 0)).is_ok()); } let check = |revs: &'a [Signature], sec: HashAlgoSecurity| -> Option> { let revs = revs.iter().filter_map(|rev| { if let Err(err) = policy.signature(rev, sec) { t!(" revocation rejected by caller policy: {}", err); None } else if hard_revocations_are_final && rev.reason_for_revocation() .map(|(r, _)| { r.revocation_type() == RevocationType::Hard }) // If there is no Reason for Revocation // packet, assume that it is a hard // revocation. .unwrap_or(true) { t!(" got a hard revocation: {:?}, {:?}", rev.signature_creation_time() .unwrap_or_else(time_zero), rev.reason_for_revocation() .map(|r| (r.0, String::from_utf8_lossy(r.1)))); Some(rev) } else if selfsig_creation_time > rev.signature_creation_time().unwrap_or_else(time_zero) { // This comes after the hard revocation check, // because a hard revocation is always valid. t!(" newer binding signature trumps soft revocation ({:?} > {:?})", selfsig_creation_time, rev.signature_creation_time().unwrap_or_else(time_zero)); None } else if let Err(err) = rev.signature_alive(t, time::Duration::new(0, 0)) { // This comes after the hard revocation check, // because a hard revocation is always valid. t!(" revocation not alive ({:?} - {:?}): {}", rev.signature_creation_time().unwrap_or_else(time_zero), rev.signature_validity_period() .unwrap_or_else(|| time::Duration::new(0, 0)), err); None } else { t!(" got a revocation: {:?} ({:?})", rev.signature_creation_time().unwrap_or_else(time_zero), rev.reason_for_revocation() .map(|r| (r.0, String::from_utf8_lossy(r.1)))); Some(rev) } }).collect::>(); if revs.len() == 0 { None } else { Some(revs) } }; if let Some(revs) = check(&self.self_revocations, self.hash_algo_security) { RevocationStatus::Revoked(revs) } else if let Some(revs) = check(&self.other_revocations, Default::default()) { RevocationStatus::CouldBe(revs) } else { RevocationStatus::NotAsFarAsWeKnow } } /// Turns the `ComponentBundle` into an iterator over its /// `Packet`s. /// /// The signatures are ordered as follows: /// /// - Self revocations, /// - Self signatures, /// - Third-party signatures, and /// - Third-party revocations. /// /// For a given type of signature, the signatures are ordered by /// their creation time, most recent first. /// /// When turning the `Key` in a `KeyBundle` into a `Packet`, this /// function uses the component's type (`C`) to determine the /// packet's type; the type is not a function of whether the key /// has secret key material. pub(crate) fn into_packets<'a>(self) -> impl Iterator + Send + Sync where Packet: From { let p : Packet = self.component.into(); std::iter::once(p) .chain(self.self_revocations.into_iter().map(|s| s.into())) .chain(self.self_signatures.into_iter().map(|s| s.into())) .chain(self.certifications.into_iter().map(|s| s.into())) .chain(self.other_revocations.into_iter().map(|s| s.into())) } // Sorts and dedups the binding's signatures. // // Note: this uses Signature::normalized_eq to compare signatures. // That function ignores unhashed packets. If there are two // signatures that only differ in their unhashed subpackets, they // will be deduped. The unhashed areas are merged as discussed in // Signature::merge. pub(crate) fn sort_and_dedup(&mut self) { // `same_bucket` function for Vec::dedup_by that compares // signatures and merges them if they are equal modulo // unhashed subpackets. fn sig_merge(a: &mut Signature, b: &mut Signature) -> bool { // If a == b, a is removed. Hence, we merge into b. if a.normalized_eq(b) { b.merge_internal(a) .expect("checked for equality above"); true } else { false } } // If two signatures are merged, we also do some fixups. Make // sure we also do this to signatures that are not merged, so // that `cert.merge(cert) == cert`. fn sig_fixup(sig: &mut Signature) { // Add missing issuer information. This is a best effort // though. If the unhashed area is full, there is nothing // we can do. let _ = sig.add_missing_issuers(); // Merging Signatures sorts the unhashed subpacket area. // Do the same. sig.unhashed_area_mut().sort(); } self.self_signatures.sort_by(Signature::normalized_cmp); self.self_signatures.dedup_by(sig_merge); // Order self signatures so that the most recent one comes // first. self.self_signatures.sort_by(sig_cmp); self.self_signatures.iter_mut().for_each(sig_fixup); self.certifications.sort_by(Signature::normalized_cmp); self.certifications.dedup_by(sig_merge); // There is no need to sort the certifications, but doing so // has the advantage that the most recent ones (and therefore // presumably the more relevant ones) come first. Also, // cert::test::signature_order checks that the signatures are // sorted. self.certifications.sort_by(sig_cmp); self.certifications.iter_mut().for_each(sig_fixup); self.self_revocations.sort_by(Signature::normalized_cmp); self.self_revocations.dedup_by(sig_merge); self.self_revocations.sort_by(sig_cmp); self.self_revocations.iter_mut().for_each(sig_fixup); self.other_revocations.sort_by(Signature::normalized_cmp); self.other_revocations.dedup_by(sig_merge); self.other_revocations.sort_by(sig_cmp); self.other_revocations.iter_mut().for_each(sig_fixup); } } impl ComponentBundle> { /// Returns a reference to the key. /// /// This is just a type-specific alias for /// [`ComponentBundle::component`]. /// /// [`ComponentBundle::component`]: #method.component /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// # /// # fn main() -> openpgp::Result<()> { /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display some information about the keys. /// for ka in cert.keys() { /// eprintln!(" - {:?}", ka.key()); /// } /// # Ok(()) } /// ``` pub fn key(&self) -> &Key { self.component() } /// Returns a mut reference to the key. pub(crate) fn key_mut(&mut self) -> &mut Key { self.component_mut() } } impl ComponentBundle> { /// Returns the subkey's revocation status at time `t`. /// /// A subkey is revoked at time `t` if: /// /// - There is a live revocation at time `t` that is newer than /// all live self signatures at time `t`, or /// /// - There is a hard revocation (even if it is not live at /// time `t`, and even if there is a newer self-signature). /// /// Note: Certs and subkeys have different criteria from User IDs /// and User Attributes. /// /// Note: this only returns whether this subkey is revoked; it /// does not imply anything about the Cert or other components. /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display the subkeys' revocation status. /// for ka in cert.keys().subkeys() { /// eprintln!(" Revocation status of {}: {:?}", /// ka.fingerprint(), ka.revocation_status(p, None)); /// } /// # Ok(()) } /// ``` pub fn revocation_status(&self, policy: &dyn Policy, t: T) -> RevocationStatus where T: Into> { let t = t.into(); self._revocation_status(policy, t, true, self.binding_signature(policy, t).ok()) } } impl ComponentBundle { /// Returns a reference to the User ID. /// /// This is just a type-specific alias for /// [`ComponentBundle::component`]. /// /// [`ComponentBundle::component`]: #method.component /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// # /// # fn main() -> openpgp::Result<()> { /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display some information about the User IDs. /// for ua in cert.userids() { /// eprintln!(" - {:?}", ua.userid()); /// } /// # Ok(()) } /// ``` pub fn userid(&self) -> &UserID { self.component() } /// Returns the User ID's revocation status at time `t`. /// /// /// /// A User ID is revoked at time `t` if: /// /// - There is a live revocation at time `t` that is newer than /// all live self signatures at time `t`. /// /// Note: Certs and subkeys have different criteria from User IDs /// and User Attributes. /// /// Note: this only returns whether this User ID is revoked; it /// does not imply anything about the Cert or other components. // /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display the User IDs' revocation status. /// for ua in cert.userids() { /// eprintln!(" Revocation status of {}: {:?}", /// String::from_utf8_lossy(ua.userid().value()), /// ua.revocation_status(p, None)); /// } /// # Ok(()) } /// ``` pub fn revocation_status(&self, policy: &dyn Policy, t: T) -> RevocationStatus where T: Into> { let t = t.into(); self._revocation_status(policy, t, false, self.binding_signature(policy, t).ok()) } } impl ComponentBundle { /// Returns a reference to the User Attribute. /// /// This is just a type-specific alias for /// [`ComponentBundle::component`]. /// /// [`ComponentBundle::component`]: #method.component /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// # /// # fn main() -> openpgp::Result<()> { /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display some information about the User Attributes /// for ua in cert.user_attributes() { /// eprintln!(" - {:?}", ua.user_attribute()); /// } /// # Ok(()) } /// ``` pub fn user_attribute(&self) -> &UserAttribute { self.component() } /// Returns the User Attribute's revocation status at time `t`. /// /// A User Attribute is revoked at time `t` if: /// /// - There is a live revocation at time `t` that is newer than /// all live self signatures at time `t`. /// /// Note: Certs and subkeys have different criteria from User IDs /// and User Attributes. /// /// Note: this only returns whether this User Attribute is revoked; /// it does not imply anything about the Cert or other components. // /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// use openpgp::policy::StandardPolicy; /// # /// # fn main() -> openpgp::Result<()> { /// let p = &StandardPolicy::new(); /// /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display the User Attributes' revocation status. /// for (i, ua) in cert.user_attributes().enumerate() { /// eprintln!(" Revocation status of User Attribute #{}: {:?}", /// i, ua.revocation_status(p, None)); /// } /// # Ok(()) } /// ``` pub fn revocation_status(&self, policy: &dyn Policy, t: T) -> RevocationStatus where T: Into> { let t = t.into(); self._revocation_status(policy, t, false, self.binding_signature(policy, t).ok()) } } impl ComponentBundle { /// Returns a reference to the unknown component. /// /// This is just a type-specific alias for /// [`ComponentBundle::component`]. /// /// [`ComponentBundle::component`]: #method.component /// /// # Examples /// /// ``` /// # use sequoia_openpgp as openpgp; /// # use openpgp::cert::prelude::*; /// # /// # fn main() -> openpgp::Result<()> { /// # let (cert, _) = /// # CertBuilder::general_purpose(None, Some("alice@example.org")) /// # .generate()?; /// // Display some information about the User Attributes /// for u in cert.unknowns() { /// eprintln!(" - {:?}", u.unknown()); /// } /// # Ok(()) } /// ``` pub fn unknown(&self) -> &Unknown { self.component() } }