diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-01-17 16:40:45 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-01-18 14:26:31 +0100 |
commit | 526c9baa08028d99b2157cd2f3d5087fa6133c86 (patch) | |
tree | b257ff16f4e3a6a3c60eef8ed77feaf198ec6655 | |
parent | 0f2575df2bbb1483762ddb14223a6e4194792a86 (diff) |
openpgp: Add KeyIter::{components,subkeys}, make subkeys() private.
- See #414.
-rw-r--r-- | guide/src/chapter_03.md | 6 | ||||
-rw-r--r-- | openpgp/src/cert/keyiter.rs | 202 | ||||
-rw-r--r-- | openpgp/src/cert/mod.rs | 4 | ||||
-rw-r--r-- | openpgp/src/cert/parser/mod.rs | 7 | ||||
-rw-r--r-- | openpgp/src/cert/revoke.rs | 4 |
5 files changed, 207 insertions, 16 deletions
diff --git a/guide/src/chapter_03.md b/guide/src/chapter_03.md index 1ced76cb..70f8676b 100644 --- a/guide/src/chapter_03.md +++ b/guide/src/chapter_03.md @@ -52,10 +52,10 @@ fn main() { &"Ἀριστοτέλης".into()); // Iterate over subkeys. - assert_eq!(cert.subkeys().count(), 2); - assert_eq!(cert.subkeys().nth(0).unwrap().key().fingerprint().to_string(), + assert_eq!(cert.keys().subkeys().count(), 2); + assert_eq!(cert.keys().subkeys().nth(0).unwrap().key().fingerprint().to_string(), "67A4 8753 A380 A6B3 B7DF 7DC5 E6C6 897A 4CEF 8924"); - assert_eq!(cert.subkeys().nth(1).unwrap().key().fingerprint().to_string(), + assert_eq!(cert.keys().subkeys().nth(1).unwrap().key().fingerprint().to_string(), "185C DAA1 2723 0423 19E4 7F67 108F 2CAF 9034 356D"); } ``` diff --git a/openpgp/src/cert/keyiter.rs b/openpgp/src/cert/keyiter.rs index 52892873..29ce5485 100644 --- a/openpgp/src/cert/keyiter.rs +++ b/openpgp/src/cert/keyiter.rs @@ -1,5 +1,5 @@ use std::fmt; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::time::SystemTime; use std::borrow::Borrow; @@ -10,9 +10,12 @@ use crate::{ packet::Key, packet::key::SecretKeyMaterial, types::KeyFlags, - Cert, - cert::KeyBindingIter, - cert::KeyAmalgamation, + cert::{ + Cert, + KeyBinding, + KeyBindingIter, + KeyAmalgamation, + }, }; /// An iterator over all `Key`s (both the primary key and the subkeys) @@ -283,6 +286,53 @@ impl<'a, P: 'a + key::KeyParts, R: 'a + key::KeyRole> KeyIter<'a, P, R> _r: self._r, } } + + /// Changes the iterator to return key components. + /// + /// A key component is similar to a key amalgamation, but is not + /// bound to a specific time. It contains the key and all + /// relevant signatures. + /// + /// If the primary key satisfies the current filter on this + /// iterator, it is returned first. + pub fn components(self) -> KeyComponentIter<'a, P, key::UnspecifiedRole> { + KeyComponentIter { + cert: self.cert, + primary: self.primary, + subkey_iter: self.subkey_iter, + + // The filters. + secret: self.secret, + unencrypted_secret: self.unencrypted_secret, + key_handles: self.key_handles, + + _p: std::marker::PhantomData, + _r: std::marker::PhantomData, + } + } + + /// Changes the iterator to return subkey components. + /// + /// A key component is similar to a key amalgamation, but is not + /// bound to a specific time. It contains the key and all + /// relevant signatures. + /// + /// The primary key is never returned from this iterator. + pub fn subkeys(self) -> KeyComponentIter<'a, P, key::SubordinateRole> { + KeyComponentIter { + cert: self.cert, + primary: true, + subkey_iter: self.subkey_iter, + + // The filters. + secret: self.secret, + unencrypted_secret: self.unencrypted_secret, + key_handles: self.key_handles, + + _p: std::marker::PhantomData, + _r: std::marker::PhantomData, + } + } } /// An iterator over all valid `Key`s in a certificate. @@ -706,6 +756,150 @@ impl<'a, P: 'a + key::KeyParts, R: 'a + key::KeyRole> ValidKeyIter<'a, P, R> } } +pub struct KeyComponentIter<'a, P: key::KeyParts, R: key::KeyRole> { + // This is an option to make it easier to create an empty KeyIter. + cert: Option<&'a Cert>, + primary: bool, + subkey_iter: KeyBindingIter<'a, + key::PublicParts, + key::SubordinateRole>, + // If not None, filters by whether a key has a secret. + secret: Option<bool>, + + // If not None, filters by whether a key has an unencrypted + // secret. + unencrypted_secret: Option<bool>, + + // Only return keys in this set. + key_handles: Vec<KeyHandle>, + + _p: std::marker::PhantomData<P>, + _r: std::marker::PhantomData<R>, +} + +impl<'a, P: key::KeyParts, R: key::KeyRole> fmt::Debug + for KeyComponentIter<'a, P, R> +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("KeyComponentIter") + .field("primary", &self.primary) + .field("secret", &self.secret) + .field("unencrypted_secret", &self.unencrypted_secret) + .field("key_handles", &self.key_handles) + .finish() + } +} + +// Very carefully implement Iterator for +// Key<{PublicParts,UnspecifiedParts}, _>. We cannot just abstract +// over the parts, because then we cannot specialize the +// implementation for Key<SecretParts, _> below. +macro_rules! impl_key_component_iterator { + ($parts:path) => { + impl<'a, R: 'a + key::KeyRole> Iterator for KeyComponentIter<'a, $parts, R> + where &'a KeyBinding<$parts, R>: + From<&'a KeyBinding<key::PublicParts, key::UnspecifiedRole>> + { + type Item = &'a KeyBinding<$parts, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.next_common().map(|b| b.into()) + } + } + } +} +impl_key_component_iterator!(key::PublicParts); +impl_key_component_iterator!(key::UnspecifiedParts); + +impl<'a, R: 'a + key::KeyRole, E> Iterator for KeyComponentIter<'a, key::SecretParts, R> + where &'a KeyBinding<key::SecretParts, R>: + TryFrom<&'a KeyBinding<key::PublicParts, key::UnspecifiedRole>, + Error = E>, + E: std::fmt::Debug, +{ + type Item = &'a KeyBinding<key::SecretParts, R>; + + fn next(&mut self) -> Option<Self::Item> { + self.next_common().map(|ka| ka.try_into().expect("has secret parts")) + } +} + +impl<'a, P: 'a + key::KeyParts, R: 'a + key::KeyRole> KeyComponentIter<'a, P, R> +{ + fn next_common(&mut self) -> Option<&'a KeyBinding<key::PublicParts, key::UnspecifiedRole>> + { + tracer!(false, "KeyComponentIter::next", 0); + t!("KeyComponentIter: {:?}", self); + + if self.cert.is_none() { + return None; + } + let cert = self.cert.unwrap(); + + loop { + let binding = + if ! self.primary { + self.primary = true; + cert.primary.mark_role_unspecified_ref() + } else { + self.subkey_iter.next()?.mark_role_unspecified_ref() + }; + + let key = binding.key(); + t!("Considering key: {:?}", key); + + if self.key_handles.len() > 0 { + if !self.key_handles + .iter() + .any(|h| h.aliases(key.key_handle())) + { + t!("{} is not one of the keys that we are looking for ({:?})", + key.key_handle(), self.key_handles); + continue; + } + } + + + if let Some(want_secret) = self.secret { + if key.secret().is_some() { + // We have a secret. + if ! want_secret { + t!("Have a secret... skipping."); + continue; + } + } else { + if want_secret { + t!("No secret... skipping."); + continue; + } + } + } + + if let Some(want_unencrypted_secret) = self.unencrypted_secret { + if let Some(secret) = key.secret() { + if let SecretKeyMaterial::Unencrypted { .. } = secret { + if ! want_unencrypted_secret { + t!("Unencrypted secret... skipping."); + continue; + } + } else { + if want_unencrypted_secret { + t!("Encrypted secret... skipping."); + continue; + } + } + } else { + // No secret. + t!("No secret... skipping."); + continue; + } + } + + return Some(binding); + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/openpgp/src/cert/mod.rs b/openpgp/src/cert/mod.rs index c52c346f..fb83fd2b 100644 --- a/openpgp/src/cert/mod.rs +++ b/openpgp/src/cert/mod.rs @@ -742,7 +742,7 @@ type UnknownBindings = ComponentBindings<Unknown>; /// } /// /// // Subkeys and related signatures. -/// for c in cert.subkeys() { +/// for c in cert.keys().subkeys() { /// acc.push(c.key().clone().into()); /// for s in c.self_signatures() { acc.push(s.clone().into()) } /// for s in c.certifications() { acc.push(s.clone().into()) } @@ -1189,7 +1189,7 @@ impl Cert { /// Returns an iterator over the Cert's valid subkeys. /// /// A valid `KeyBinding` has at least one good self-signature. - pub fn subkeys(&self) -> KeyBindingIter<key::PublicParts, + pub(crate) fn subkeys(&self) -> KeyBindingIter<key::PublicParts, key::SubordinateRole> { KeyBindingIter { iter: Some(self.subkeys.iter()) } diff --git a/openpgp/src/cert/parser/mod.rs b/openpgp/src/cert/parser/mod.rs index 739125a4..d944fea7 100644 --- a/openpgp/src/cert/parser/mod.rs +++ b/openpgp/src/cert/parser/mod.rs @@ -520,11 +520,8 @@ impl<'a, I: Iterator<Item=Packet>> CertParser<'a, I> { /// # let some_keyid = KeyID::from_hex("C2B819056C652598").unwrap(); /// for certr in CertParser::from_packet_parser(ppr) /// .unvalidated_cert_filter(|cert, _| { - /// if cert.primary().keyid() == some_keyid { - /// return true; - /// } - /// for binding in cert.subkeys() { - /// if binding.key().keyid() == some_keyid { + /// for component in cert.keys().components() { + /// if component.key().keyid() == some_keyid { /// return true; /// } /// } diff --git a/openpgp/src/cert/revoke.rs b/openpgp/src/cert/revoke.rs index 59ed1284..d7e892a0 100644 --- a/openpgp/src/cert/revoke.rs +++ b/openpgp/src/cert/revoke.rs @@ -163,7 +163,7 @@ impl Deref for CertRevocationBuilder { /// .generate()?; /// let mut keypair = cert.primary().clone() /// .mark_parts_secret()?.into_keypair()?; -/// let subkey = cert.subkeys().nth(0).unwrap(); +/// let subkey = cert.keys().subkeys().nth(0).unwrap(); /// /// // Generate the revocation for the first and only Subkey. /// let revocation = @@ -178,7 +178,7 @@ impl Deref for CertRevocationBuilder { /// let cert = cert.merge_packets(vec![revocation.clone().into()])?; /// /// // Check that it is revoked. -/// let subkey = cert.subkeys().nth(0).unwrap(); +/// let subkey = cert.keys().subkeys().nth(0).unwrap(); /// if let RevocationStatus::Revoked(revocations) = subkey.revoked(None) { /// assert_eq!(revocations.len(), 1); /// assert_eq!(*revocations[0], revocation); |