diff options
author | Neal H. Walfield <neal@pep.foundation> | 2023-01-02 01:03:23 +0100 |
---|---|---|
committer | Neal H. Walfield <neal@pep.foundation> | 2023-01-06 15:00:23 +0100 |
commit | c86c9152fa90052370e605e8422ffac96aa509e8 (patch) | |
tree | 0d00456a0930999d073bf94faca9b171b518ab61 | |
parent | cb0adbb4baa4a397d7763608cc08cd26782b3eb6 (diff) |
openpgp: Add convenient accessor functions to RawCert.
- The main reason to use a `RawCertParser` is to avoid having to
parse certificates that are definitely not needed in the current
context.
- Add some convenient accessor functions to `RawCert`:
`RawCert::primary_key`, `RawCert::keys`, `RawCert::subkeys`, and
`RawCert::UserID` to make this easier.
-rw-r--r-- | openpgp/src/cert/raw.rs | 168 | ||||
-rw-r--r-- | openpgp/src/cert/raw/iter.rs | 173 |
2 files changed, 341 insertions, 0 deletions
diff --git a/openpgp/src/cert/raw.rs b/openpgp/src/cert/raw.rs index 91abc924..a8cda44c 100644 --- a/openpgp/src/cert/raw.rs +++ b/openpgp/src/cert/raw.rs @@ -86,14 +86,19 @@ use crate::packet::Header; use crate::packet::Key; use crate::packet::Packet; use crate::packet::Tag; +use crate::packet::UserID; use crate::packet::header::BodyLength; use crate::packet::header::CTB; +use crate::packet::key; use crate::parse::PacketParser; use crate::parse::Parse; use crate::parse::RECOVERY_THRESHOLD; use super::TRACE; +mod iter; +pub use iter::KeyIter; + /// A mostly unparsed `Packet`. /// /// This is returned by [`RawCert::packets`]. @@ -308,6 +313,132 @@ impl<'a> RawCert<'a> { pub fn count(&self) -> usize { self.packets.len() } + + /// Returns an iterator over the certificate's keys. + /// + /// Note: this parses the key packets, but it does not verify any + /// binding signatures. As such, this can only be used as part of + /// a precheck. If the certificate appears to match, then the + /// caller must convert the [`RawCert`] to a [`Cert`] or a + /// [`ValidCert`], depending on the requirements, and perform the + /// check again. + /// + /// [`ValidCert`]: crate::cert::ValidCert + /// + /// Use [`subkeys`] to just return the subkeys. This function + /// also changes the return type. Instead of the iterator + /// returning a [`Key`] whose role is [`key::UnspecifiedRole`], + /// the role is [`key::SubordinateRole`]. + /// + /// [`subkeys`]: KeyIter::subkeys + /// + /// # Examples + /// + /// ```rust + /// use sequoia_openpgp as openpgp; + // + /// # use openpgp::Result; + /// # use openpgp::cert::prelude::*; + /// use openpgp::cert::raw::RawCertParser; + /// use openpgp::parse::Parse; + /// # use openpgp::serialize::Serialize; + /// # + /// # fn main() -> Result<()> { + /// # let (cert, _) = CertBuilder::new() + /// # .add_signing_subkey() + /// # .add_certification_subkey() + /// # .add_transport_encryption_subkey() + /// # .add_storage_encryption_subkey() + /// # .add_authentication_subkey() + /// # .generate()?; + /// # + /// # let mut bytes = Vec::new(); + /// # cert.serialize(&mut bytes); + /// # let mut certs = 0; + /// # let mut keys = 0; + /// for cert in RawCertParser::from_bytes(&bytes)? { + /// /// Ignore corrupt and invalid certificates. + /// let cert = if let Ok(cert) = cert { + /// cert + /// } else { + /// continue; + /// }; + /// + /// // Iterate over the keys. Note: this parses the Key + /// // packets. + /// for key in cert.keys() { + /// println!("{}", key.fingerprint()); + /// # keys += 1; + /// } + /// # certs += 1; + /// } + /// # assert_eq!(certs, 1); + /// # assert_eq!(keys, 6); + /// # Ok(()) + /// # } + /// ``` + pub fn keys(&self) -> KeyIter<key::PublicParts, key::UnspecifiedRole> { + KeyIter::new(self) + } + + // Returns an iterator over the certificate's keys. + // + // This is used by `KeyIter`, which implements a number of + // filters. + fn keys_internal(&self) + -> impl Iterator<Item=Key<key::PublicParts, key::UnspecifiedRole>> + '_ + { + self.packets() + .filter_map(|p| { + if matches!(p.tag(), + Tag::PublicKey | Tag::PublicSubkey + | Tag::SecretKey | Tag::SecretSubkey) + { + Key::from_bytes(p.body()) + .ok() + .map(|k| k.parts_into_public()) + } else { + None + } + }) + } + + /// Returns the certificate's primary key. + /// + /// Note: this parses the primary key packet, but it does not + /// verify any binding signatures. As such, this can only be used + /// as part of a precheck. If the certificate appears to match, + /// then the caller must convert the [`RawCert`] to a [`Cert`] or + /// a [`ValidCert`], depending on the requirements, and perform + /// the check again. + /// + /// [`ValidCert`]: crate::cert::ValidCert + pub fn primary_key(&self) -> Key<key::PublicParts, key::PrimaryRole> { + self.keys().next().expect("have a primary key").role_into_primary() + } + + /// Returns the certificate's User IDs. + /// + /// Note: this parses the User ID packets, but it does not verify + /// any binding signatures. That is, there is no guarantee that + /// the User IDs should actually be associated with the primary + /// key. As such, this can only be used as part of a precheck. + /// If a User ID appears to match, then the caller must convert + /// the [`RawCert`] to a [`Cert`] or a [`ValidCert`], depending on + /// the requirements, and perform the check again. + /// + /// [`ValidCert`]: crate::cert::ValidCert + pub fn userids(&self) -> impl Iterator<Item=UserID> + '_ + { + self.packets() + .filter_map(|p| { + if p.tag() == Tag::UserID { + UserID::try_from(p.body()).ok() + } else { + None + } + }) + } } impl<'a> TryFrom<&RawCert<'a>> for Cert { @@ -1352,4 +1483,41 @@ mod test { Ok(()) } + + #[test] + fn accessors() { + let testy = crate::tests::key("testy.pgp"); + + let certs = RawCertParser::from_bytes(testy) + .expect("valid") + .collect::<Result<Vec<RawCert>>>() + .expect("valid"); + assert_eq!(certs.len(), 1); + let cert = &certs[0]; + assert_eq!(cert.as_bytes(), testy); + + assert_eq!(cert.primary_key().fingerprint(), + "3E8877C877274692975189F5D03F6F865226FE8B" + .parse().expect("valid")); + assert_eq!(cert.keys().map(|k| k.fingerprint()).collect::<Vec<_>>(), + vec![ + "3E8877C877274692975189F5D03F6F865226FE8B" + .parse().expect("valid"), + "01F187575BD45644046564C149E2118166C92632" + .parse().expect("valid") + ]); + assert_eq!(cert.keys().subkeys() + .map(|k| k.fingerprint()).collect::<Vec<_>>(), + vec![ + "01F187575BD45644046564C149E2118166C92632" + .parse().expect("valid") + ]); + assert_eq!( + cert.userids() + .map(|u| { + String::from_utf8_lossy(u.value()).into_owned() + }) + .collect::<Vec<_>>(), + vec![ "Testy McTestface <testy@example.org>" ]); + } } diff --git a/openpgp/src/cert/raw/iter.rs b/openpgp/src/cert/raw/iter.rs new file mode 100644 index 00000000..85d4da5c --- /dev/null +++ b/openpgp/src/cert/raw/iter.rs @@ -0,0 +1,173 @@ +use std::fmt; + +use crate::cert::raw::RawCert; +use crate::packet::Key; +use crate::packet::key; + +/// A key iterator for `RawCert`s. +/// +/// This is returned by [`RawCert::keys`]. It is analogous to +/// [`KeyAmalgamationIter`], but for `RawCert`s. +/// +/// [`KeyAmalgamationIter`]: crate::cert::amalgamation::key::KeyAmalgamationIter +pub struct KeyIter<'a, P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + key_iter: Box<dyn Iterator<Item=Key<key::PublicParts, + key::UnspecifiedRole>> + + Send + Sync + 'a>, + + // Whether the primary key has been returned. + returned_primary: bool, + // Whether the primary key should be returned. + want_primary: bool, + + _p: std::marker::PhantomData<P>, + _r: std::marker::PhantomData<R>, +} +assert_send_and_sync!(KeyIter<'_, P, R> + where P: key::KeyParts, + R: key::KeyRole, +); + +impl<'a, P, R> fmt::Debug for KeyIter<'a, P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("KeyIter") + .field("want_primary", &self.want_primary) + .finish() + } +} + +macro_rules! impl_iterator { + ($parts:path, $role:path, $item:ty) => { + impl<'a> Iterator for KeyIter<'a, $parts, $role> + { + type Item = $item; + + fn next(&mut self) -> Option<Self::Item> { + // We unwrap the result of the conversion. But, this + // is safe by construction: next_common only returns + // keys that can be correctly converted. + self.next_common() + } + } + } +} + +impl_iterator!(key::PublicParts, key::UnspecifiedRole, + Key<key::PublicParts, key::UnspecifiedRole>); +impl_iterator!(key::PublicParts, key::SubordinateRole, + Key<key::PublicParts, key::SubordinateRole>); + +impl<'a, P, R> KeyIter<'a, P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + fn next_common(&mut self) -> Option<Key<P, R>> + { + tracer!(false, "KeyIter::next", 0); + t!("{:?}", self); + + loop { + if ! self.returned_primary { + if ! self.want_primary { + // Discard the primary key. + let _ = self.key_iter.next(); + } + self.returned_primary = true; + } + + let key = self.key_iter.next()? + .parts_into_unspecified() + .role_into_unspecified(); + let key = if let Ok(key) = P::convert_key(key) { + key + } else { + // The caller wants secret keys, but this is no secret + // key, skip it. + continue; + }; + let key = R::convert_key(key); + + // Apply any filters. + + return Some(key); + } + } +} + +impl<'a, P, R> KeyIter<'a, P, R> + where P: key::KeyParts, + R: key::KeyRole, +{ + /// Returns a new `KeyIter` instance. + pub(crate) fn new(cert: &'a RawCert) -> Self where Self: 'a { + KeyIter { + key_iter: Box::new(cert.keys_internal()), + + want_primary: true, + returned_primary: false, + + _p: std::marker::PhantomData, + _r: std::marker::PhantomData, + } + } + + /// Changes the iterator to only return subkeys. + /// + /// This function also changes the return type. Instead of the + /// iterator returning a [`Key`] whose role is + /// [`key::UnspecifiedRole`], the role is [`key::SubordinateRole`] + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp as openpgp; + /// # use openpgp::Result; + /// # use openpgp::cert::prelude::*; + /// # use openpgp::cert::raw::RawCertParser; + /// # use openpgp::parse::Parse; + /// # use openpgp::serialize::Serialize; + /// # + /// # fn main() -> Result<()> { + /// # let (cert, _) = CertBuilder::new() + /// # .add_signing_subkey() + /// # .add_certification_subkey() + /// # .add_transport_encryption_subkey() + /// # .add_storage_encryption_subkey() + /// # .add_authentication_subkey() + /// # .generate()?; + /// # + /// # let mut bytes = Vec::new(); + /// # cert.serialize(&mut bytes); + /// # let mut parser = RawCertParser::from_bytes(&bytes)?; + /// # + /// # let rawcert = parser.next().expect("have one").expect("valid"); + /// # assert!(parser.next().is_none()); + /// # let mut i = 0; + /// for subkey in rawcert.keys().subkeys() { + /// // Use it. + /// println!("{}", subkey.fingerprint()); + /// # i += 1; + /// } + /// # assert_eq!(i, 5); + /// # Ok(()) + /// # } + /// ``` + /// + pub fn subkeys(self) -> KeyIter<'a, P, key::SubordinateRole> { + KeyIter { + key_iter: self.key_iter, + + want_primary: false, + returned_primary: self.returned_primary, + + _p: std::marker::PhantomData, + _r: std::marker::PhantomData, + } + } +} |