diff options
-rw-r--r-- | openpgp-ffi/include/sequoia/openpgp.h | 103 | ||||
-rw-r--r-- | openpgp-ffi/src/tpk.rs | 186 | ||||
-rw-r--r-- | openpgp/examples/decrypt-with.rs | 2 | ||||
-rw-r--r-- | openpgp/examples/notarize.rs | 2 | ||||
-rw-r--r-- | openpgp/examples/sign-detached.rs | 2 | ||||
-rw-r--r-- | openpgp/examples/sign.rs | 2 | ||||
-rw-r--r-- | openpgp/src/crypto/keygrip.rs | 2 | ||||
-rw-r--r-- | openpgp/src/packet/key_flags.rs | 10 | ||||
-rw-r--r-- | openpgp/src/packet/signature/mod.rs | 9 | ||||
-rw-r--r-- | openpgp/src/parse/stream.rs | 12 | ||||
-rw-r--r-- | openpgp/src/serialize/stream.rs | 8 | ||||
-rw-r--r-- | openpgp/src/tpk/mod.rs | 417 | ||||
-rw-r--r-- | openpgp/src/tsk.rs | 50 | ||||
-rw-r--r-- | sqv/src/sqv.rs | 16 | ||||
-rw-r--r-- | store/src/backend/mod.rs | 2 | ||||
-rw-r--r-- | tool/src/commands/mod.rs | 9 | ||||
-rw-r--r-- | tool/tests/sq-sign.rs | 2 |
17 files changed, 682 insertions, 152 deletions
diff --git a/openpgp-ffi/include/sequoia/openpgp.h b/openpgp-ffi/include/sequoia/openpgp.h index 1efd7d8a..b4115388 100644 --- a/openpgp-ffi/include/sequoia/openpgp.h +++ b/openpgp-ffi/include/sequoia/openpgp.h @@ -503,12 +503,86 @@ pgp_signature_t pgp_user_id_binding_selfsig(pgp_user_id_binding_t binding); /*/ pgp_user_id_binding_t pgp_user_id_binding_iter_next (pgp_user_id_binding_iter_t iter); +/*/ /// Frees an pgp_user_id_binding_iter_t. +/*/ void pgp_user_id_binding_iter_free (pgp_user_id_binding_iter_t iter); /* openpgp::tpk::KeyIter. */ /*/ +/// Changes the iterator to only return keys that are certification +/// capable. +/// +/// If you call this function and, e.g., the `signing_capable` +/// function, the *union* of the values is used. That is, the +/// iterator will return keys that are certification capable *or* +/// signing capable. +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_certification_capable (pgp_tpk_key_iter_t iter); + +/*/ +/// Changes the iterator to only return keys that are certification +/// capable. +/// +/// If you call this function and, e.g., the `signing_capable` +/// function, the *union* of the values is used. That is, the +/// iterator will return keys that are certification capable *or* +/// signing capable. +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_signing_capable (pgp_tpk_key_iter_t iter); + +/*/ +/// Changes the iterator to only return keys that are alive. +/// +/// If you call this function (or `pgp_tpk_key_iter_alive_at`), only +/// the last value is used. +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_alive (pgp_tpk_key_iter_t iter); + +/*/ +/// Changes the iterator to only return keys that are alive at the +/// specified time. +/// +/// If you call this function (or `pgp_tpk_key_iter_alive`), only the +/// last value is used. +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_alive_at (pgp_tpk_key_iter_t iter, time_t when); + +/*/ +/// Changes the iterator to only return keys whose revocation status +/// matches `revoked`. +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_revoked (pgp_tpk_key_iter_t iter, bool revoked); + +/*/ +/// Changes the iterator to only return keys that have secret keys (or +/// not). +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_secret (pgp_tpk_key_iter_t iter, bool secret); + +/*/ +/// Changes the iterator to only return keys that have unencrypted +/// secret keys (or not). +/// +/// Note: you may not call this function after starting to iterate. +/*/ +void pgp_tpk_key_iter_unencrypted_secret (pgp_tpk_key_iter_t iter, + bool unencrypted_secret); + +/*/ /// Returns the next key. Returns NULL if there are no more elements. /// /// If sigo is not NULL, stores the current self-signature (if any) in @@ -718,10 +792,33 @@ int pgp_tpk_is_tsk(pgp_tpk_t tpk); pgp_user_id_binding_iter_t pgp_tpk_user_id_binding_iter (pgp_tpk_t tpk); /*/ -/// Returns an iterator over all `Key`s (both the primary key and any -/// subkeys) in a TPK. +/// Returns an iterator over the live and unrevoked `Key`s in a TPK. +/// +/// Compare with `pgp_tpk_keys_iter_valid`, which doesn'filters out +/// expired and revoked keys by default. +/*/ +pgp_tpk_key_iter_t pgp_tpk_keys_iter_all (pgp_tpk_t tpk); + +/*/ +/// Returns an iterator over all `Key`s in a TPK. +/// +/// That is, this returns an iterator over the primary key and any +/// subkeys, along with the corresponding signatures. +/// +/// Note: since a primary key is different from a subkey, the iterator +/// is over `Key`s and not `SubkeyBindings`. Since the primary key +/// has no binding signature, the signature carrying the primary key's +/// key flags is returned (either a direct key signature, or the +/// self-signature on the primary User ID). There are corner cases +/// where no such signature exists (e.g. partial TPKs), therefore this +/// iterator may return `None` for the primary key's signature. +/// +/// A valid `Key` has at least one good self-signature. +/// +/// Compare with `pgp_tpk_keys_iter_all`, which doesn't filter out +/// expired and revoked keys. /*/ -pgp_tpk_key_iter_t pgp_tpk_key_iter (pgp_tpk_t tpk); +pgp_tpk_key_iter_t pgp_tpk_keys_iter_valid (pgp_tpk_t tpk); /*/ /// Returns the TPK's primary user id (if any). diff --git a/openpgp-ffi/src/tpk.rs b/openpgp-ffi/src/tpk.rs index 1483c71c..39315565 100644 --- a/openpgp-ffi/src/tpk.rs +++ b/openpgp-ffi/src/tpk.rs @@ -437,19 +437,51 @@ pub extern "system" fn pgp_user_id_binding_iter_next<'a>( pub struct KeyIterWrapper<'a> { iter: KeyIter<'a>, rso: Option<RevocationStatus<'a>>, + // Whether next has been called. + next_called: bool, } -/// Returns an iterator over the TPK's keys. +/// Returns an iterator over the TPK's live, non-revoked keys. /// -/// This iterates over both the primary key and any subkeys. +/// That is, this returns an iterator over the primary key and any +/// subkeys, along with the corresponding signatures. +/// +/// Note: since a primary key is different from a subkey, the iterator +/// is over `Key`s and not `SubkeyBindings`. Since the primary key +/// has no binding signature, the signature carrying the primary key's +/// key flags is returned (either a direct key signature, or the +/// self-signature on the primary User ID). There are corner cases +/// where no such signature exists (e.g. partial TPKs), therefore this +/// iterator may return `None` for the primary key's signature. +/// +/// A valid `Key` has at least one good self-signature. +/// +/// To return all keys, use `pgp_tpk_keys_all_iter()`. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_keys_iter_valid(tpk: *const TPK) + -> *mut KeyIterWrapper<'static> +{ + let tpk = tpk.ref_raw(); + box_raw!(KeyIterWrapper { + iter: tpk.keys_valid(), + rso: None, + next_called: false, + }) +} + +/// Returns an iterator over all `Key`s in a TPK. +/// +/// Compare with `pgp_tpk_keys_iter_all`, which doesn't filter out +/// expired and revoked keys by default. #[::sequoia_ffi_macros::extern_fn] #[no_mangle] -pub extern "system" fn pgp_tpk_key_iter(tpk: *const TPK) +pub extern "system" fn pgp_tpk_keys_iter_all(tpk: *const TPK) -> *mut KeyIterWrapper<'static> { let tpk = tpk.ref_raw(); box_raw!(KeyIterWrapper { - iter: tpk.keys(), + iter: tpk.keys_all(), rso: None, + next_called: false, }) } @@ -461,6 +493,151 @@ pub extern "system" fn pgp_tpk_key_iter_free( ffi_free!(iter) } +/// Changes the iterator to only return keys that are certification +/// capable. +/// +/// If you call this function and, e.g., the `signing_capable` +/// function, the *union* of the values is used. That is, the +/// iterator will return keys that are certification capable *or* +/// signing capable. +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_certification_capable<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.certification_capable(); +} + +/// Changes the iterator to only return keys that are certification +/// capable. +/// +/// If you call this function and, e.g., the `signing_capable` +/// function, the *union* of the values is used. That is, the +/// iterator will return keys that are certification capable *or* +/// signing capable. +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_signing_capable<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.signing_capable(); +} + +/// Changes the iterator to only return keys that are alive. +/// +/// If you call this function (or `pgp_tpk_key_iter_alive_at`), only +/// the last value is used. +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_alive<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.alive(); +} + +/// Changes the iterator to only return keys that are alive at the +/// specified time. +/// +/// If you call this function (or `pgp_tpk_key_iter_alive`), only the +/// last value is used. +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_alive_at<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>, + when: time_t) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.alive_at(time::at(time::Timespec::new(when as i64, 0))); +} + +/// Changes the iterator to only return keys whose revocation status +/// matches `revoked`. +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_revoked<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>, + revoked: bool) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.revoked(Some(revoked)); +} + +/// Changes the iterator to only return keys that have secret keys (or +/// not). +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_secret<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>, + secret: bool) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.secret(Some(secret)); +} + +/// Changes the iterator to only return keys that have unencrypted +/// secret keys (or not). +/// +/// Note: you may not call this function after starting to iterate. +#[::sequoia_ffi_macros::extern_fn] #[no_mangle] +pub extern "system" fn pgp_tpk_key_iter_unencrypted_secret<'a>( + iter_wrapper: *mut KeyIterWrapper<'a>, + unencrypted_secret: bool) +{ + let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); + if iter_wrapper.next_called { + panic!("Can't change KeyIter filter after iterating."); + } + + use std::mem; + let tmp = mem::replace(&mut iter_wrapper.iter, KeyIter::empty()); + iter_wrapper.iter = tmp.unencrypted_secret(Some(unencrypted_secret)); +} + /// Returns the next key. Returns NULL if there are no more elements. /// /// If sigo is not NULL, stores the current self-signature (if any) in @@ -479,6 +656,7 @@ pub extern "system" fn pgp_tpk_key_iter_next<'a>( { let iter_wrapper = ffi_param_ref_mut!(iter_wrapper); iter_wrapper.rso = None; + iter_wrapper.next_called = true; if let Some((sig, rs, key)) = iter_wrapper.iter.next() { if let Some(ptr) = sigo { diff --git a/openpgp/examples/decrypt-with.rs b/openpgp/examples/decrypt-with.rs index 176e03de..a5b26baa 100644 --- a/openpgp/examples/decrypt-with.rs +++ b/openpgp/examples/decrypt-with.rs @@ -57,7 +57,7 @@ impl Helper { // Map (sub)KeyIDs to secrets. let mut keys = HashMap::new(); for tpk in tpks { - for (sig, _, key) in tpk.keys() { + for (sig, _, key) in tpk.keys_all() { if sig.map(|s| (s.key_flags().can_encrypt_at_rest() || s.key_flags().can_encrypt_for_transport())) .unwrap_or(false) diff --git a/openpgp/examples/notarize.rs b/openpgp/examples/notarize.rs index 5214ad9e..1b849376 100644 --- a/openpgp/examples/notarize.rs +++ b/openpgp/examples/notarize.rs @@ -30,7 +30,7 @@ fn main() { let tsk = openpgp::TPK::from_file(filename) .expect("Failed to read key"); - for key in tsk.select_signing_keys(None) { + for key in tsk.keys_valid().signing_capable().map(|k| k.2) { if let Some(mut secret) = key.secret() { let secret_mpis = match secret { SecretKey::Encrypted { .. } => { diff --git a/openpgp/examples/sign-detached.rs b/openpgp/examples/sign-detached.rs index 19dc771f..e558aa5e 100644 --- a/openpgp/examples/sign-detached.rs +++ b/openpgp/examples/sign-detached.rs @@ -25,7 +25,7 @@ fn main() { let tsk = openpgp::TPK::from_file(filename) .expect("Failed to read key"); - for key in tsk.select_signing_keys(None) { + for key in tsk.keys_valid().signing_capable().map(|k| k.2) { if let Some(mut secret) = key.secret() { let secret_mpis = match secret { SecretKey::Encrypted { .. } => { diff --git a/openpgp/examples/sign.rs b/openpgp/examples/sign.rs index c3ee0728..33e7625e 100644 --- a/openpgp/examples/sign.rs +++ b/openpgp/examples/sign.rs @@ -25,7 +25,7 @@ fn main() { let tsk = openpgp::TPK::from_file(filename) .expect("Failed to read key"); - for key in tsk.select_signing_keys(None) { + for key in tsk.keys_valid().signing_capable().map(|k| k.2) { if let Some(mut secret) = key.secret() { let secret_mpis = match secret { SecretKey::Encrypted { .. } => { diff --git a/openpgp/src/crypto/keygrip.rs b/openpgp/src/crypto/keygrip.rs index 50a2448c..3d1a84bd 100644 --- a/openpgp/src/crypto/keygrip.rs +++ b/openpgp/src/crypto/keygrip.rs @@ -340,7 +340,7 @@ mod tests { .iter().map(|n| (n, ::TPK::from_file(path_to(n)).unwrap())) { eprintln!("{}", name); - for key in tpk.keys() { + for key in tpk.keys_all() { let fp = key.2.fingerprint(); eprintln!("(sub)key: {}", fp); assert_eq!(&key.2.mpis().keygrip().unwrap(), diff --git a/openpgp/src/packet/key_flags.rs b/openpgp/src/packet/key_flags.rs index 92dac82f..51f9e00d 100644 --- a/openpgp/src/packet/key_flags.rs +++ b/openpgp/src/packet/key_flags.rs @@ -156,6 +156,11 @@ impl KeyFlags { } } + /// Returns a new `KeyFlags` with all capabilities disabled. + pub fn empty() -> Self { + KeyFlags::default() + } + /// Returns a slice referencing the raw values. pub(crate) fn as_vec(&self) -> Vec<u8> { let mut ret = if self.unknown.is_empty() { @@ -249,6 +254,11 @@ impl KeyFlags { self.is_group_key = v; self } + + /// Returns whether no flags are set. + pub fn is_empty(&self) -> bool { + self.as_vec().into_iter().all(|b| b == 0) + } } // Numeric key capability flags. diff --git a/openpgp/src/packet/signature/mod.rs b/openpgp/src/packet/signature/mod.rs index 2d877c56..8097d149 100644 --- a/openpgp/src/packet/signature/mod.rs +++ b/openpgp/src/packet/signature/mod.rs @@ -1094,12 +1094,15 @@ mod test { #[test] fn verify_gpg_3rd_party_cert() { - use {packet::KeyFlags, TPK}; + use TPK; - let cert_kf = KeyFlags::default().set_certify(true); let test1 = TPK::from_file( path_to("keys/test1-certification-key.pgp")).unwrap(); - let cert_key1 = test1.select_keys(cert_kf, None)[0]; + let cert_key1 = test1.keys_all() + .certification_capable() + .nth(0) + .map(|x| x.2) + .unwrap(); let test2 = TPK::from_file( path_to("keys/test2-signed-by-test1.pgp")).unwrap(); let uid_binding = &test2.primary_key_signature_full().unwrap().0.unwrap(); diff --git a/openpgp/src/parse/stream.rs b/openpgp/src/parse/stream.rs index a89d61a3..fcea6038 100644 --- a/openpgp/src/parse/stream.rs +++ b/openpgp/src/parse/stream.rs @@ -118,7 +118,7 @@ const BUFFER_SIZE: usize = 25 * 1024 * 1024; pub struct Verifier<'a, H: VerificationHelper> { helper: H, tpks: Vec<TPK>, - /// Maps KeyID to tpks[i].keys().nth(j). + /// Maps KeyID to tpks[i].keys_all().nth(j). keys: HashMap<KeyID, (usize, usize)>, oppr: Option<PacketParserResult<'a>>, sigs: Vec<Vec<VerificationResult>>, @@ -350,7 +350,8 @@ impl<'a, H: VerificationHelper> Verifier<'a, H> { if let Some(issuer) = sig.get_issuer() { if let Some((i, j)) = self.keys.get(&issuer) { - let (_, _, key) = self.tpks[*i].keys().nth(*j).unwrap(); + let (_, _, key) + = self.tpks[*i].keys_all().nth(*j).unwrap(); if sig.verify(key).unwrap_or(false) { self.sigs.iter_mut().last() .expect("sigs is never empty").push( @@ -808,7 +809,7 @@ impl DetachedVerifier { pub struct Decryptor<'a, H: VerificationHelper + DecryptionHelper> { helper: H, tpks: Vec<TPK>, - /// Maps KeyID to tpks[i].keys().nth(j). + /// Maps KeyID to tpks[i].keys_all().nth(j). keys: HashMap<KeyID, (usize, usize)>, oppr: Option<PacketParserResult<'a>>, identity: Option<Fingerprint>, @@ -1167,7 +1168,8 @@ impl<'a, H: VerificationHelper + DecryptionHelper> Decryptor<'a, H> { if let Some(issuer) = sig.get_issuer() { if let Some((i, j)) = self.keys.get(&issuer) { - let (_, _, key) = self.tpks[*i].keys().nth(*j).unwrap(); + let (_, _, key) + = self.tpks[*i].keys_all().nth(*j).unwrap(); if sig.verify(key).unwrap_or(false) { self.sigs.iter_mut().last() .expect("sigs is never empty").push( @@ -1488,7 +1490,7 @@ mod test { // sign 30MiB message let mut buf = vec![]; { - let key = tpk.select_signing_keys(None)[0]; + let key = tpk.keys_all().signing_capable().nth(0).unwrap().2; let sec = match key.secret() { Some(SecretKey::Unencrypted { ref mpis }) => mpis, _ => unreachable!(), diff --git a/openpgp/src/serialize/stream.rs b/openpgp/src/serialize/stream.rs index 5e88db65..1d3c79e1 100644 --- a/openpgp/src/serialize/stream.rs +++ b/openpgp/src/serialize/stream.rs @@ -228,7 +228,7 @@ impl<'a> Signer<'a> { /// # let tsk = TPK::from_bytes(include_bytes!( /// # "../../tests/data/keys/testy-new-private.pgp")) /// # .unwrap(); - /// # let key = tsk.select_signing_keys(None)[0]; + /// # let key = tsk.keys_valid().signing_capable().nth(0).unwrap().2; /// # let sec = match key.secret() { /// # Some(SecretKey::Unencrypted { ref mpis }) => mpis, /// # _ => unreachable!(), @@ -284,7 +284,7 @@ impl<'a> Signer<'a> { /// # let tsk = TPK::from_bytes(include_bytes!( /// # "../../tests/data/keys/testy-new-private.pgp")) /// # .unwrap(); - /// # let key = tsk.select_signing_keys(None)[0]; + /// # let key = tsk.keys_valid().signing_capable().nth(0).unwrap().2; /// # let sec = match key.secret() { /// # Some(SecretKey::Unencrypted { ref mpis }) => mpis, /// # _ => unreachable!(), @@ -1282,7 +1282,6 @@ mod test { #[test] fn signature() { use crypto::KeyPair; - use packet::KeyFlags; use packet::key::SecretKey; use std::collections::HashMap; use Fingerprint; @@ -1292,8 +1291,7 @@ mod test { TPK::from_bytes(bytes!("keys/testy-private.pgp")).unwrap(), TPK::from_bytes(bytes!("keys/testy-new-private.pgp")).unwrap(), ] { - for key in tsk.select_keys( - KeyFlags::default().set_sign(true), None) + for key in tsk.keys_all().signing_capable().map(|x| x.2) { keys.insert(key.fingerprint(), key.clone()); } diff --git a/openpgp/src/tpk/mod.rs b/openpgp/src/tpk/mod.rs index eca1448b..394f9922 100644 --- a/openpgp/src/tpk/mod.rs +++ b/openpgp/src/tpk/mod.rs @@ -20,6 +20,7 @@ use { packet::Tag, packet::signature::{self, Signature}, packet::Key, + packet::key::SecretKey, packet::UserID, packet::UserAttribute, packet::Unknown, @@ -861,32 +862,312 @@ pub struct UnknownBinding { /// in a TPK. /// /// Returned by TPK::keys(). +/// +/// `KeyIter` follows the builder pattern. There is no need to +/// explicitly finalize it, however: it already implements the +/// `Iterator` interface. +/// +/// By default, `KeyIter` will only return live, non-revoked keys. It +/// is possible to control how `KeyIter` filters using, for instance, +/// `KeyIter::flags` to only return keys with particular flags set. pub struct KeyIter<'a> { - tpk: &'a TPK, + // This is an option to make it easier to create an empty KeyIter. + tpk: Option<&'a TPK>, primary: bool, subkey_iter: SubkeyBindingIter<'a>, + + // If not None, only returns keys with the specified flags. + flags: Option<KeyFlags>, + + // If not None, only returns keys that are live at the specified + // time. + alive_at: Option<time::Tm>, + + // If not None, filters by revocation status. + revoked: Option<bool>, + + // 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>, +} + +impl<'a> fmt::Debug for KeyIter<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("KeyIter") + .field("flags", &self.flags) + .field("alive_at", &self.alive_at) + .field("revoked", &self.revoked) + .field("secret", &self.secret) + .field("unencrypted_secret", &self.unencrypted_secret) + .finish() + } } impl<'a> Iterator for KeyIter<'a> { type Item = (Option<&'a Signature>, RevocationStatus<'a>, &'a Key); fn next(&mut self) -> Option<Self::Item> { - if ! self.primary { - self.primary = true; - |