summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2023-01-02 01:03:23 +0100
committerNeal H. Walfield <neal@pep.foundation>2023-01-06 15:00:23 +0100
commitc86c9152fa90052370e605e8422ffac96aa509e8 (patch)
tree0d00456a0930999d073bf94faca9b171b518ab61
parentcb0adbb4baa4a397d7763608cc08cd26782b3eb6 (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.rs168
-rw-r--r--openpgp/src/cert/raw/iter.rs173
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,
+ }
+ }
+}