diff options
Diffstat (limited to 'openpgp/src/cert/mod.rs')
-rw-r--r-- | openpgp/src/cert/mod.rs | 3115 |
1 files changed, 3115 insertions, 0 deletions
diff --git a/openpgp/src/cert/mod.rs b/openpgp/src/cert/mod.rs new file mode 100644 index 00000000..7817063d --- /dev/null +++ b/openpgp/src/cert/mod.rs @@ -0,0 +1,3115 @@ +//! OpenPGP Certificates. + +use std::io; +use std::cmp; +use std::cmp::Ordering; +use std::path::Path; +use std::slice; +use std::mem; +use std::fmt; +use std::ops::{Deref, DerefMut}; +use std::time; + +use crate::{ + conversions::Time, + crypto::{hash::Hash, Signer}, + Error, + Result, + RevocationStatus, + SignatureType, + HashAlgorithm, + packet, + packet::Signature, + packet::signature, + packet::Key, + packet::key, + packet::UserID, + packet::UserAttribute, + packet::Unknown, + Packet, + PacketPile, + KeyID, + Fingerprint, +}; +use crate::parse::{Parse, PacketParserResult, PacketParser}; +use crate::types::{ + ReasonForRevocation, + RevocationType, +}; + +mod builder; +mod bindings; +mod keyiter; +mod parser; +mod revoke; + +pub use self::builder::{CertBuilder, CipherSuite}; + +pub use keyiter::KeyIter; + +pub use parser::{ + KeyringValidity, + KeyringValidator, + CertParser, + CertValidity, + CertValidator, +}; + +pub use revoke::{ + SubkeyRevocationBuilder, + CertRevocationBuilder, + UserAttributeRevocationBuilder, + UserIDRevocationBuilder, +}; + +const TRACE : bool = false; + +// Helper functions. + +/// Compare the creation time of two signatures. Order them so that +/// the more recent signature is first. +fn canonical_signature_order(a: Option<time::SystemTime>, b: Option<time::SystemTime>) + -> Ordering { + // Note: None < Some, so the normal ordering is: + // + // None, Some(old), Some(new) + // + // Reversing the ordering puts the signatures without a creation + // time at the end, which is where they belong. + a.cmp(&b).reverse() +} + +fn sig_cmp(a: &Signature, b: &Signature) -> Ordering { + match canonical_signature_order(a.signature_creation_time(), + b.signature_creation_time()) { + Ordering::Equal => a.mpis().cmp(b.mpis()), + r => r + } +} + +/// A key (primary or subkey, public or private) and any associated +/// signatures. +pub type KeyBinding<KeyPart, KeyRole> = ComponentBinding<Key<KeyPart, KeyRole>>; + +impl<K: key::KeyParts, R: key::KeyRole> KeyBinding<K, R> +{ + /// Gets the key packet's `SecretKeyMaterial`. + /// + /// Note: The key module installs conversion functions on + /// KeyBinding. They need to access the key's secret. + pub(crate) fn secret(&self) + -> Option<&crate::packet::key::SecretKeyMaterial> { + self.key().secret() + } +} + +/// A primary key and any associated signatures. +pub type PrimaryKeyBinding<KeyPart> = KeyBinding<KeyPart, key::PrimaryRole>; + +/// A subkey and any associated signatures. +pub type SubkeyBinding<KeyPart> = KeyBinding<KeyPart, key::SubordinateRole>; + +/// A key (primary or subkey, public or private) and any associated +/// signatures. +pub type GenericKeyBinding + = ComponentBinding<Key<key::UnspecifiedParts, key::UnspecifiedRole>>; + +/// A User ID and any associated signatures. +pub type UserIDBinding = ComponentBinding<UserID>; + +/// A User Attribute and any associated signatures. +pub type UserAttributeBinding = ComponentBinding<UserAttribute>; + +/// An unknown component and any associated signatures. +/// +/// Note: all signatures are stored as certifications. +pub type UnknownBinding = ComponentBinding<Unknown>; + +/// A Cert component binding. +/// +/// A Cert component is a primary key, a subkey, a user id, or a user +/// attribute. A binding is a Cert component and any related +/// signatures. +#[derive(Debug, Clone, PartialEq)] +pub struct ComponentBinding<C> { + component: C, + + // Self signatures. + self_signatures: Vec<Signature>, + + // Third-party certifications. (In general, this will only be by + // designated revokers.) + certifications: Vec<Signature>, + + // Self revocations. + self_revocations: Vec<Signature>, + + // Third-party revocations (e.g., designated revokers). + other_revocations: Vec<Signature>, +} + +impl<C> ComponentBinding<C> { + /// Returns a reference to the component. + 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`. + /// + /// An active binding signature is a non-revoked, self-signature + /// that is alive at time `t` (`creation time <= t`, `t <= + /// expiry`). + /// + /// This function returns None if there are no active binding + /// signatures at time `t`. + pub fn binding_signature<T>(&self, t: T) -> Option<&Signature> + where T: Into<Option<time::SystemTime>> + { + let t = t.into().unwrap_or_else(|| time::SystemTime::now().canonicalize()); + + // 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. 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) => { + // XXX: we use PartialOrd to compare Tms due to + // https://github.com/rust-lang-deprecated/time/issues/180 + while i > 0 + && self.self_signatures[i - 1].signature_creation_time() + .cmp(&Some(t)) == Ordering::Equal + { + 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, + } + }; + + self.self_signatures[i..].iter().filter(|s| { + s.signature_alive(t, time::Duration::new(0, 0)) + }).nth(0) + } + + /// The self-signatures. + /// + /// The signatures are validated, and they are reverse sorted by + /// their creation time (newest first). + pub fn self_signatures(&self) -> &[Signature] { + &self.self_signatures + } + + /// Any third-party certifications. + /// + /// The signatures are *not* validated. They are reverse sorted by + /// their creation time (newest first). + pub fn certifications(&self) -> &[Signature] { + &self.certifications + } + + /// Revocations issued by the key itself. + /// + /// The revocations are validated, and they are reverse sorted by + /// their creation time (newest first). + pub fn self_revocations(&self) -> &[Signature] { + &self.self_revocations + } + + /// Revocations issued by other keys. + /// + /// The revocations are *not* validated. They are reverse sorted + /// by their creation time (newest first). + 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 live at time `t`, and even + /// if there is a newer self-signature). + /// + /// selfsig must be the newest live self signature at time `t`. + fn _revoked<'a, T>(&'a self, hard_revocations_are_final: bool, + selfsig: Option<&Signature>, t: T) + -> RevocationStatus<'a> + where T: Into<Option<time::SystemTime>> + { + // Fallback time. + let time_zero = || time::UNIX_EPOCH; + let t = t.into() + .unwrap_or_else(|| time::SystemTime::now().canonicalize()); + let selfsig_creation_time + = selfsig.and_then(|s| s.signature_creation_time()) + .unwrap_or_else(time_zero); + + tracer!(TRACE, "ComponentBinding::_revoked", 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))); + } + + macro_rules! check { + ($revs:expr) => ({ + let revs = $revs.iter().filter_map(|rev| { + 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) + { + t!(" ignoring out of date revocation ({:?})", + rev.signature_creation_time() + .unwrap_or_else(time_zero)); + None + } else if !rev.signature_alive(t, time::Duration::new(0, 0)) { + t!(" ignoring revocation that is not alive ({:?} - {:?})", + rev.signature_creation_time() + .unwrap_or_else(time_zero), + rev.signature_expiration_time() + .unwrap_or_else(|| time::Duration::new(0, 0))); + 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::<Vec<&Signature>>(); + + if revs.len() == 0 { + None + } else { + Some(revs) + } + }) + } + + if let Some(revs) = check!(&self.self_revocations) { + RevocationStatus::Revoked(revs) + } else if let Some(revs) = check!(&self.other_revocations) { + RevocationStatus::CouldBe(revs) + } else { + RevocationStatus::NotAsFarAsWeKnow + } + } + + // Converts the component into an iterator over the contained + // packets. + fn into_packets<'a>(self) -> impl Iterator<Item=Packet> + where Packet: From<C> + { + let p : Packet = self.component.into(); + std::iter::once(p) + .chain(self.self_signatures.into_iter().map(|s| s.into())) + .chain(self.certifications.into_iter().map(|s| s.into())) + .chain(self.self_revocations.into_iter().map(|s| s.into())) + .chain(self.other_revocations.into_iter().map(|s| s.into())) + } + + // Sorts and dedups the binding's signatures. + // + // This function assumes that the signatures have already been + // cryptographically checked. + // + // Note: this uses Signature::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 *not* merged; the one that is + // kept is undefined. + fn sort_and_dedup(&mut self) + { + self.self_signatures.sort_by(sig_cmp); + self.self_signatures.dedup(); + + // There is no need to sort the certifications, but we do + // want to remove dups and sorting is a prerequisite. + self.certifications.sort_by(sig_cmp); + self.certifications.dedup(); + + self.self_revocations.sort_by(sig_cmp); + self.self_revocations.dedup(); + + self.other_revocations.sort_by(sig_cmp); + self.other_revocations.dedup(); + } +} + +impl<P: key::KeyParts, R: key::KeyRole> ComponentBinding<Key<P, R>> { + /// Returns a reference to the key. + pub fn key(&self) -> &Key<P, R> { + self.component() + } + + /// Returns a mut reference to the key. + fn key_mut(&mut self) -> &mut Key<P, R> { + self.component_mut() + } +} + +impl<P: key::KeyParts> ComponentBinding<Key<P, key::SubordinateRole>> { + /// 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. + pub fn revoked<T>(&self, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(true, self.binding_signature(t), t) + } +} + +impl ComponentBinding<UserID> { + /// Returns a reference to the User ID. + 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`, or + /// + /// 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. + pub fn revoked<T>(&self, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(false, self.binding_signature(t), t) + } +} + +impl ComponentBinding<UserAttribute> { + /// Returns a reference to the User Attribute. + 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`, or + /// + /// 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. + pub fn revoked<T>(&self, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(false, self.binding_signature(t), t) + } +} + +impl ComponentBinding<Unknown> { + /// Returns a reference to the unknown component. + pub fn unknown(&self) -> &Unknown { + self.component() + } +} + + +impl fmt::Display for Cert { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.primary().fingerprint()) + } +} + +/// An iterator over `ComponentBinding`s. +pub struct ComponentBindingIter<'a, C> { + iter: Option<slice::Iter<'a, ComponentBinding<C>>>, +} + +/// An iterator over `KeyBinding`s. +pub type KeyBindingIter<'a, P, R> = ComponentBindingIter<'a, Key<P, R>>; +/// An iterator over `UserIDBinding`s. +pub type UserIDBindingIter<'a> = ComponentBindingIter<'a, UserID>; +/// An iterator over `UserAttributeBinding`s. +pub type UserAttributeBindingIter<'a> = ComponentBindingIter<'a, UserAttribute>; +/// An iterator over `UnknownBinding`s. +pub type UnknownBindingIter<'a> = ComponentBindingIter<'a, Unknown>; + +impl<'a, C> Iterator for ComponentBindingIter<'a, C> +{ + type Item = &'a ComponentBinding<C>; + + fn next(&mut self) -> Option<Self::Item> { + match self.iter { + Some(ref mut iter) => iter.next(), + None => None, + } + } +} + +impl<'a, C> ExactSizeIterator for ComponentBindingIter<'a, C> +{ + fn len(&self) -> usize { + match self.iter { + Some(ref iter) => iter.len(), + None => 0, + } + } +} + +/// A collection of `ComponentBindings`. +/// +/// Note: we need this, because we can't `impl Vec<ComponentBindings>`. +#[derive(Debug, Clone, PartialEq)] +pub struct ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + bindings: Vec<ComponentBinding<C>>, +} + +impl<C> Deref for ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + type Target = Vec<ComponentBinding<C>>; + + fn deref(&self) -> &Self::Target { + &self.bindings + } +} + +impl<C> DerefMut for ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + fn deref_mut(&mut self) -> &mut Vec<ComponentBinding<C>> { + &mut self.bindings + } +} + +impl<C> Into<Vec<ComponentBinding<C>>> for ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + fn into(self) -> Vec<ComponentBinding<C>> { + self.bindings + } +} + +impl<C> IntoIterator for ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + type Item = ComponentBinding<C>; + type IntoIter = std::vec::IntoIter<Self::Item>; + + fn into_iter(self) -> Self::IntoIter { + self.bindings.into_iter() + } +} + +impl<C> ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + fn new() -> Self { + Self { bindings: vec![] } + } +} + +impl<C> ComponentBindings<C> + where ComponentBinding<C>: cmp::PartialEq +{ + // Sort and dedup the components. + // + // `cmp` is a function to sort the components for deduping. + // + // `merge` is a function that merges the first component into the + // second component. + fn sort_and_dedup<F, F2>(&mut self, cmp: F, merge: F2) + where F: Fn(&C, &C) -> Ordering, + F2: Fn(&mut C, &mut C) + { + // We dedup by component (not bindings!). To do this, we need + // to sort the bindings by their components. + + self.bindings.sort_unstable_by( + |a, b| cmp(&a.component, &b.component)); + + self.bindings.dedup_by(|a, b| { + if cmp(&a.component, &b.component) == Ordering::Equal { + // Merge. + merge(&mut a.component, &mut b.component); + + // Recall: if a and b are equal, a will be dropped. + b.self_signatures.append(&mut a.self_signatures); + b.certifications.append(&mut a.certifications); + b.self_revocations.append(&mut a.self_revocations); + b.other_revocations.append(&mut a.self_revocations); + + true + } else { + false + } + }); + + // And sort the certificates. + for b in self.bindings.iter_mut() { + b.sort_and_dedup(); + } + } +} + +/// A vecor of key (primary or subkey, public or private) and any +/// associated signatures. +pub type KeyBindings<KeyPart, KeyRole> = ComponentBindings<Key<KeyPart, KeyRole>>; + +/// A vector of subkeys and any associated signatures. +pub type SubkeyBindings<KeyPart> = KeyBindings<KeyPart, key::SubordinateRole>; + +/// A vector of key (primary or subkey, public or private) and any +/// associated signatures. +pub type GenericKeyBindings + = ComponentBindings<Key<key::UnspecifiedParts, key::UnspecifiedRole>>; + +/// A vector of User ID bindings and any associated signatures. +pub type UserIDBindings = ComponentBindings<UserID>; + +/// A vector of User Attribute bindings and any associated signatures. +pub type UserAttributeBindings = ComponentBindings<UserAttribute>; + +/// A vector of unknown components and any associated signatures. +/// +/// Note: all signatures are stored as certifications. +pub type UnknownBindings = ComponentBindings<Unknown>; + + +/// A OpenPGP Certificate. +/// +/// A Certificate (see [RFC 4880, section 11.1]) can be used to verify +/// signatures and encrypt data. It can be stored in a keystore and +/// uploaded to keyservers. +/// +/// Certs are always canonicalized in the sense that only elements +/// (user id, user attribute, subkey) with at least one valid +/// self-signature are preserved. Also, invalid self-signatures are +/// dropped. The self-signatures are sorted so that the newest +/// self-signature comes first. Components are sorted, but in an +/// undefined manner (i.e., when parsing the same Cert multiple times, +/// the components will be in the same order, but we reserve the right +/// to change the sort function between versions). Third-party +/// certifications are *not* validated, as the keys are not available; +/// they are simply passed through as is. +/// +/// [RFC 4880, section 11.1]: https://tools.ietf.org/html/rfc4880#section-11.1 +/// +/// # Secret keys +/// +/// Any key in a `Cert` may have a secret key attached to it. To +/// protect secret keys from being leaked, secret keys are not written +/// out if a `Cert` is serialized. To also serialize the secret keys, +/// you need to use [`Cert::as_tsk()`] to get an object that writes +/// them out during serialization. +/// +/// [`Cert::as_tsk()`]: #method.as_tsk +/// +/// # Example +/// +/// ```rust +/// # extern crate sequoia_openpgp as openpgp; +/// # use openpgp::Result; +/// # use openpgp::parse::{Parse, PacketParserResult, PacketParser}; +/// use openpgp::Cert; +/// +/// # fn main() { f().unwrap(); } +/// # fn f() -> Result<()> { +/// # let ppr = PacketParser::from_bytes(&b""[..])?; +/// match Cert::from_packet_parser(ppr) { +/// Ok(cert) => { +/// println!("Key: {}", cert.primary()); +/// for binding in cert.userids() { +/// println!("User ID: {}", binding.userid()); +/// } +/// } +/// Err(err) => { +/// eprintln!("Error parsing Cert: {}", err); +/// } +/// } +/// +/// # Ok(()) +/// # } +#[derive(Debug, Clone, PartialEq)] +pub struct Cert { + primary: PrimaryKeyBinding<key::PublicParts>, + + userids: UserIDBindings, + user_attributes: UserAttributeBindings, + subkeys: SubkeyBindings<key::PublicParts>, + + // Unknown components, e.g., some UserAttribute++ packet from the + // future. + unknowns: UnknownBindings, + // Signatures that we couldn't find a place for. + bad: Vec<packet::Signature>, +} + +impl std::str::FromStr for Cert { + type Err = failure::Error; + + fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { + Self::from_bytes(s.as_bytes()) + } +} + +impl<'a> Parse<'a, Cert> for Cert { + /// Returns the first Cert encountered in the reader. + fn from_reader<R: io::Read>(reader: R) -> Result<Self> { + Cert::from_packet_parser(PacketParser::from_reader(reader)?) + } + + /// Returns the first Cert encountered in the file. + fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> { + Cert::from_packet_parser(PacketParser::from_file(path)?) + } + + /// Returns the first Cert found in `buf`. + /// + /// `buf` must be an OpenPGP-encoded message. + fn from_bytes<D: AsRef<[u8]> + ?Sized>(data: &'a D) -> Result<Self> { + Cert::from_packet_parser(PacketParser::from_bytes(data)?) + } +} + +impl Cert { + /// Returns a reference to the primary key binding. + /// + /// Note: information about the primary key is often stored on the + /// primary User ID's self signature. Since these signatures are + /// associated with the UserID and not the primary key, that + /// information is not contained in the key binding. Instead, you + /// should use methods like `Cert::primary_key_signature()` to get + /// information about the primary key. + pub fn primary(&self) -> &key::PublicKey { + &self.primary.key() + } + + /// Returns the binding for the primary User ID at time `t`. + /// + /// See `Cert::primary_userid_full` for a description of how the + /// primary user id is determined. + pub fn primary_userid<T>(&self, t: T) -> Option<&UserIDBinding> + where T: Into<Option<time::SystemTime>> + { + self.primary_userid_full(t).map(|r| r.0) + } + + /// Returns the binding for the primary User ID at time `t` and + /// some associated data. + /// + /// In addition to the User ID binding, this also returns the + /// binding signature and the User ID's `RevocationStatus` at time + /// `t`. + /// + /// The primary User ID is determined by taking the User IDs that + /// are alive at time `t`, and sorting them as follows: + /// + /// - non-revoked first + /// - primary first + /// - signature creation first + /// + /// If there is more than one, than one is selected in a + /// deterministic, but undefined manner. + pub fn primary_userid_full<T>(&self, t: T) + -> Option<(&UserIDBinding, &Signature, RevocationStatus)> + where T: Into<Option<time::SystemTime>> + { + let t = t.into() + .unwrap_or_else(|| time::SystemTime::now().canonicalize()); + self.userids() + // Filter out User IDs that are not alive at time `t`. + // + // While we have the binding signature, extract a few + // properties to avoid recomputing the same thing multiple + // times. + .filter_map(|b| { + // No binding signature at time `t` => not alive. + let selfsig = b.binding_signature(t)?; + + if !selfsig.signature_alive(t, time::Duration::new(0, 0)) { + return None; + } + + let revoked = b.revoked(t); + let primary = selfsig.primary_userid().unwrap_or(false); |