diff options
Diffstat (limited to 'openpgp/src/cert/bundle.rs')
-rw-r--r-- | openpgp/src/cert/bundle.rs | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/openpgp/src/cert/bundle.rs b/openpgp/src/cert/bundle.rs new file mode 100644 index 00000000..15227e5b --- /dev/null +++ b/openpgp/src/cert/bundle.rs @@ -0,0 +1,512 @@ +//! Types for certificate components. + +use std::slice; +use std::time; +use std::ops::Deref; + +use crate::{ + packet::Signature, + packet::Key, + packet::key, + packet::UserID, + packet::UserAttribute, + packet::Unknown, + Packet, + policy::Policy, + Result, +}; +use crate::types::{ + RevocationType, + RevocationStatus, +}; + +use super::{ + sig_cmp, + canonical_signature_order, +}; + +pub use super::{ + component_iter::ComponentIter, + component_iter::ValidComponentIter, + keyiter::KeyIter, + keyiter::ValidKeyIter, +}; + +/// A key (primary or subkey, public or private) and any associated +/// signatures. +pub type KeyBundle<KeyPart, KeyRole> = ComponentBundle<Key<KeyPart, KeyRole>>; + +/// A primary key and any associated signatures. +pub type PrimaryKeyBundle<KeyPart> = + KeyBundle<KeyPart, key::PrimaryRole>; + +/// A subkey and any associated signatures. +pub type SubkeyBundle<KeyPart> + = KeyBundle<KeyPart, key::SubordinateRole>; + +/// A key (primary or subkey, public or private) and any associated +/// signatures. +#[allow(dead_code)] +type GenericKeyBinding + = ComponentBundle<Key<key::UnspecifiedParts, key::UnspecifiedRole>>; + +/// A User ID and any associated signatures. +pub type UserIDBundle = ComponentBundle<UserID>; + +/// A User Attribute and any associated signatures. +pub type UserAttributeBundle = ComponentBundle<UserAttribute>; + +/// An unknown component and any associated signatures. +/// +/// Note: all signatures are stored as certifications. +pub type UnknownBundle = ComponentBundle<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 ComponentBundle<C> { + pub(crate) component: C, + + // Self signatures. + pub(crate) self_signatures: Vec<Signature>, + + // Third-party certifications. (In general, this will only be by + // designated revokers.) + pub(crate) certifications: Vec<Signature>, + + // Self revocations. + pub(crate) self_revocations: Vec<Signature>, + + // Third-party revocations (e.g., designated revokers). + pub(crate) other_revocations: Vec<Signature>, +} + +impl<C> Deref for ComponentBundle<C> +{ + type Target = C; + + fn deref(&self) -> &Self::Target { + &self.component + } +} + +impl<C> ComponentBundle<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 an error if there are no active binding + /// signatures at time `t`, or there is one that did not match the + /// given policy. + pub fn binding_signature<T>(&self, policy: &dyn Policy, t: T) + -> Result<&Signature> + where T: Into<Option<time::SystemTime>> + { + 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. 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; + let mut error = crate::Error::NoBindingSignature(t).into(); + for s in self.self_signatures[i..].iter() { + if let Err(e) = s.signature_alive(t, time::Duration::new(0, 0)) { + error = e; + continue; + } + + if let Err(e) = policy.signature(s) { + error = e; + continue; + } + + sig = Some(s); + break; + } + + sig.ok_or(error) + } + + /// 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`. + pub(crate) fn _revoked<'a, T>(&'a self, policy: &dyn Policy, t: T, + hard_revocations_are_final: bool, + selfsig: Option<&Signature>) + -> 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()); + let selfsig_creation_time + = selfsig.and_then(|s| s.signature_creation_time()) + .unwrap_or_else(time_zero); + + tracer!(super::TRACE, "ComponentBundle::_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)).is_ok()); + } + + let check = |revs: &'a [Signature]| -> Option<Vec<&'a Signature>> { + let revs = revs.iter().filter_map(|rev| { + if let Err(err) = policy.signature(rev) { + 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::<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. + /// + /// The signatures are ordered from authenticated and most + /// important to not authenticated and most likely to be abused. + /// The order is: + /// + /// - Self revocations first. They are authenticated and the + /// most important information. + /// - Self signatures. They are authenticated. + /// - Other signatures. They are not authenticated at this point. + /// - Other revocations. They are not authenticated, and likely + /// not well supported in other implementations, hence the + /// least reliable way of revoking keys and therefore least + /// useful and most likely to be abused. + pub(crate) 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_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. + // + // 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. + pub(crate) 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> ComponentBundle<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. + pub(crate) fn key_mut(&mut self) -> &mut Key<P, R> { + self.component_mut() + } +} + +impl<P: key::KeyParts> ComponentBundle<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, policy: &dyn Policy, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(policy, t, true, self.binding_signature(policy, t).ok()) + } +} + +impl ComponentBundle<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, policy: &dyn Policy, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(policy, t, false, self.binding_signature(policy, t).ok()) + } +} + +impl ComponentBundle<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, policy: &dyn Policy, t: T) + -> RevocationStatus + where T: Into<Option<time::SystemTime>> + { + let t = t.into(); + self._revoked(policy, t, false, self.binding_signature(policy, t).ok()) + } +} + +impl ComponentBundle<Unknown> { + /// Returns a reference to the unknown component. + pub fn unknown(&self) -> &Unknown { + self.component() + } +} + +/// An iterator over `ComponentBundle`s. +pub struct ComponentBundleIter<'a, C> { + pub(crate) iter: Option<slice::Iter<'a, ComponentBundle<C>>>, +} + +/// An iterator over `KeyBundle`s. +pub type UnfilteredKeyBundleIter<'a, P, R> = ComponentBundleIter<'a, Key<P, R>>; +/// An iterator over `UserIDBundle`s. +pub type UserIDBundleIter<'a> = ComponentBundleIter<'a, UserID>; +/// An iterator over `UserAttributeBundle`s. +pub type UserAttributeBundleIter<'a> = ComponentBundleIter<'a, UserAttribute>; +/// An iterator over `UnknownBundle`s. +pub type UnknownBundleIter<'a> = ComponentBundleIter<'a, Unknown>; + +impl<'a, C> Iterator for ComponentBundleIter<'a, C> +{ + type Item = &'a ComponentBundle<C>; + + fn next(&mut self) -> Option<Self::Item> { + match self.iter { + Some(ref mut iter) => iter.next(), + None => None, + } + } +} + +impl<'a, C> ExactSizeIterator for ComponentBundleIter<'a, C> +{ + fn len(&self) -> usize { + match self.iter { + Some(ref iter) => iter.len(), + None => 0, + } + } +} |