summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2020-01-17 16:40:45 +0100
committerJustus Winter <justus@sequoia-pgp.org>2020-01-18 14:26:31 +0100
commit526c9baa08028d99b2157cd2f3d5087fa6133c86 (patch)
treeb257ff16f4e3a6a3c60eef8ed77feaf198ec6655
parent0f2575df2bbb1483762ddb14223a6e4194792a86 (diff)
openpgp: Add KeyIter::{components,subkeys}, make subkeys() private.
- See #414.
-rw-r--r--guide/src/chapter_03.md6
-rw-r--r--openpgp/src/cert/keyiter.rs202
-rw-r--r--openpgp/src/cert/mod.rs4
-rw-r--r--openpgp/src/cert/parser/mod.rs7
-rw-r--r--openpgp/src/cert/revoke.rs4
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);