sequoia_openpgp/cert/amalgamation/
iter.rs

1use std::slice;
2use std::fmt;
3use std::time::SystemTime;
4
5use crate::{
6    types::RevocationStatus,
7    cert::prelude::*,
8    packet::{
9        Unknown,
10        UserAttribute,
11        UserID,
12    },
13    policy::Policy,
14};
15
16/// An iterator over components.
17///
18/// Using the [`ComponentAmalgamationIter::with_policy`], it is
19/// possible to change the iterator to only return
20/// [`ComponentAmalgamation`]s for valid components.  In this case,
21/// `ComponentAmalgamationIter::with_policy` transforms the
22/// `ComponentAmalgamationIter` into a
23/// [`ValidComponentAmalgamationIter`], which returns
24/// [`ValidComponentAmalgamation`]s.  `ValidComponentAmalgamation`
25/// offers additional filters.
26///
27/// `ComponentAmalgamationIter` follows the builder pattern.  There is
28/// no need to explicitly finalize it: it already implements the
29/// `Iterator` trait.
30///
31/// A `ComponentAmalgamationIter` is returned by [`Cert::userids`],
32/// [`Cert::user_attributes`], and [`Cert::unknowns`].
33/// ([`Cert::keys`] returns a [`KeyAmalgamationIter`].)
34///
35/// # Examples
36///
37/// Iterate over the User IDs in a certificate:
38///
39/// ```
40/// # use sequoia_openpgp as openpgp;
41/// use openpgp::cert::prelude::*;
42///
43/// #
44/// # fn main() -> openpgp::Result<()> {
45/// #     let (cert, _) =
46/// #         CertBuilder::general_purpose(Some("alice@example.org"))
47/// #         .generate()?;
48/// #     let fpr = cert.fingerprint();
49/// // Iterate over all User IDs.
50/// for ua in cert.userids() {
51///     // ua is a `ComponentAmalgamation`, specifically, a `UserIDAmalgamation`.
52/// }
53/// #     Ok(())
54/// # }
55/// ```
56///
57/// Only return valid User IDs.
58///
59/// ```
60/// # use sequoia_openpgp as openpgp;
61/// use openpgp::cert::prelude::*;
62/// use openpgp::policy::StandardPolicy;
63/// #
64/// # fn main() -> openpgp::Result<()> {
65/// let p = &StandardPolicy::new();
66///
67/// #     let (cert, _) =
68/// #         CertBuilder::general_purpose(Some("alice@example.org"))
69/// #         .generate()?;
70/// #     let fpr = cert.fingerprint();
71/// // Iterate over all valid User IDs.
72/// for ua in cert.userids().with_policy(p, None) {
73///     // ua is a `ValidComponentAmalgamation`, specifically, a
74///     // `ValidUserIDAmalgamation`.
75/// }
76/// #     Ok(())
77/// # }
78/// ```
79///
80/// [`ComponentAmalgamationIter::with_policy`]: ComponentAmalgamationIter::with_policy()
81/// [`Cert::userids`]: super::Cert::userids()
82/// [`Cert::user_attributes`]: super::Cert::user_attributes()
83/// [`Cert::unknowns`]: super::Cert::unknowns()
84/// [`Cert::keys`]: super::Cert::keys()
85pub struct ComponentAmalgamationIter<'a, C> {
86    cert: &'a Cert,
87    iter: slice::Iter<'a, ComponentBundle<C>>,
88}
89assert_send_and_sync!(ComponentAmalgamationIter<'_, C> where C);
90
91/// An iterator over `UserIDAmalgamtion`s.
92///
93/// A specialized version of [`ComponentAmalgamationIter`].
94///
95pub type UserIDAmalgamationIter<'a>
96    = ComponentAmalgamationIter<'a, UserID>;
97
98/// An iterator over `UserAttributeAmalgamtion`s.
99///
100/// A specialized version of [`ComponentAmalgamationIter`].
101///
102pub type UserAttributeAmalgamationIter<'a>
103    = ComponentAmalgamationIter<'a, UserAttribute>;
104
105/// An iterator over `UnknownComponentAmalgamtion`s.
106///
107/// A specialized version of [`ComponentAmalgamationIter`].
108///
109pub type UnknownComponentAmalgamationIter<'a>
110    = ComponentAmalgamationIter<'a, Unknown>;
111
112
113impl<'a, C> fmt::Debug for ComponentAmalgamationIter<'a, C> {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        f.debug_struct("ComponentAmalgamationIter")
116            .finish()
117    }
118}
119
120impl<'a, C> Iterator for ComponentAmalgamationIter<'a, C>
121{
122    type Item = ComponentAmalgamation<'a, C>;
123
124    fn next(&mut self) -> Option<Self::Item> {
125        self.iter.next().map(|c| ComponentAmalgamation::new(self.cert, c))
126    }
127}
128
129impl<'a, C> ComponentAmalgamationIter<'a, C> {
130    /// Returns a new `ComponentAmalgamationIter` instance.
131    pub(crate) fn new(cert: &'a Cert,
132                      iter: std::slice::Iter<'a, ComponentBundle<C>>) -> Self
133        where Self: 'a
134    {
135        ComponentAmalgamationIter {
136            cert, iter,
137        }
138    }
139
140    /// Changes the iterator to only return components that are valid
141    /// according to the policy at the specified time.
142    ///
143    /// If `time` is None, then the current time is used.
144    ///
145    /// Refer to the [`ValidateAmalgamation`] trait for a definition
146    /// of a valid component.
147    ///
148    /// [`ValidateAmalgamation`]: super::ValidateAmalgamation
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// # use sequoia_openpgp as openpgp;
154    /// use openpgp::cert::prelude::*;
155    /// use openpgp::policy::StandardPolicy;
156    /// #
157    /// # fn main() -> openpgp::Result<()> {
158    /// let p = &StandardPolicy::new();
159    ///
160    /// #     let (cert, _) =
161    /// #         CertBuilder::general_purpose(Some("alice@example.org"))
162    /// #         .generate()?;
163    /// #     let fpr = cert.fingerprint();
164    /// // Iterate over all valid User Attributes.
165    /// for ua in cert.user_attributes().with_policy(p, None) {
166    ///     // ua is a `ValidComponentAmalgamation`, specifically, a
167    ///     // `ValidUserAttributeAmalgamation`.
168    /// }
169    /// #     Ok(())
170    /// # }
171    /// ```
172    ///
173    pub fn with_policy<T>(self, policy: &'a dyn Policy, time: T)
174        -> ValidComponentAmalgamationIter<'a, C>
175        where T: Into<Option<SystemTime>>
176    {
177        ValidComponentAmalgamationIter {
178            cert: self.cert,
179            iter: self.iter,
180            time: time.into().unwrap_or_else(crate::now),
181            policy,
182            revoked: None,
183        }
184    }
185}
186
187/// An iterator over valid components.
188///
189/// A `ValidComponentAmalgamationIter` is a
190/// [`ComponentAmalgamationIter`] with a policy and a reference time.
191///
192/// This allows it to filter the returned components based on
193/// information available in the components' binding signatures.  For
194/// instance, [`ValidComponentAmalgamationIter::revoked`] filters the
195/// returned components by whether they are revoked.
196///
197/// `ValidComponentAmalgamationIter` follows the builder pattern.
198/// There is no need to explicitly finalize it: it already implements
199/// the `Iterator` trait.
200///
201/// A `ValidComponentAmalgamationIter` is returned by
202/// [`ComponentAmalgamationIter::with_policy`].
203///
204/// # Examples
205///
206/// ```
207/// # use sequoia_openpgp as openpgp;
208/// use openpgp::cert::prelude::*;
209/// use openpgp::policy::StandardPolicy;
210/// #
211/// # fn main() -> openpgp::Result<()> {
212/// let p = &StandardPolicy::new();
213///
214/// #     let (cert, _) =
215/// #         CertBuilder::general_purpose(Some("alice@example.org"))
216/// #         .generate()?;
217/// #     let fpr = cert.fingerprint();
218/// // Iterate over all valid User Attributes.
219/// for ua in cert.userids().with_policy(p, None) {
220///     // ua is a `ValidComponentAmalgamation`, specifically, a
221///     // `ValidUserIDAmalgamation`.
222/// }
223/// #     Ok(())
224/// # }
225/// ```
226///
227/// [`ValidComponentAmalgamationIter::revoked`]: ValidComponentAmalgamationIter::revoked()
228/// [`ComponentAmalgamationIter::with_policy`]: ComponentAmalgamationIter::with_policy()
229pub struct ValidComponentAmalgamationIter<'a, C> {
230    // This is an option to make it easier to create an empty ValidComponentAmalgamationIter.
231    cert: &'a Cert,
232    iter: slice::Iter<'a, ComponentBundle<C>>,
233
234    policy: &'a dyn Policy,
235    // The time.
236    time: SystemTime,
237
238    // If not None, filters by whether the component is revoked or not
239    // at time `t`.
240    revoked: Option<bool>,
241}
242assert_send_and_sync!(ValidComponentAmalgamationIter<'_, C> where C);
243
244/// An iterator over `ValidUserIDAmalgamtion`s.
245///
246/// This is just a specialized version of `ValidComponentAmalgamationIter`.
247pub type ValidUserIDAmalgamationIter<'a>
248    = ValidComponentAmalgamationIter<'a, UserID>;
249
250/// An iterator over `ValidUserAttributeAmalgamtion`s.
251///
252/// This is just a specialized version of `ValidComponentAmalgamationIter`.
253pub type ValidUserAttributeAmalgamationIter<'a>
254    = ValidComponentAmalgamationIter<'a, UserAttribute>;
255
256
257impl<'a, C> fmt::Debug for ValidComponentAmalgamationIter<'a, C> {
258    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259        f.debug_struct("ValidComponentAmalgamationIter")
260            .field("time", &self.time)
261            .field("revoked", &self.revoked)
262            .finish()
263    }
264}
265
266impl<'a, C> Iterator for ValidComponentAmalgamationIter<'a, C>
267where
268    C: std::fmt::Debug + Send + Sync,
269{
270    type Item = ValidComponentAmalgamation<'a, C>;
271
272    fn next(&mut self) -> Option<Self::Item> {
273        tracer!(false, "ValidComponentAmalgamationIter::next", 0);
274        t!("ValidComponentAmalgamationIter: {:?}", self);
275
276        loop {
277            let ca = ComponentAmalgamation::new(self.cert, self.iter.next()?);
278            t!("Considering component: {:?}", ca.component());
279
280            let vca = match ca.with_policy(self.policy, self.time) {
281                Ok(vca) => vca,
282                Err(e) => {
283                    t!("Rejected: {}", e);
284                    continue;
285                },
286            };
287
288            if let Some(want_revoked) = self.revoked {
289                if let RevocationStatus::Revoked(_) = vca.revocation_status() {
290                    // The component is definitely revoked.
291                    if ! want_revoked {
292                        t!("Component revoked... skipping.");
293                        continue;
294                    }
295                } else {
296                    // The component is probably not revoked.
297                    if want_revoked {
298                        t!("Component not revoked... skipping.");
299                        continue;
300                    }
301                }
302            }
303
304            return Some(vca);
305        }
306    }
307}
308
309impl<'a, C> ExactSizeIterator for ComponentAmalgamationIter<'a, C>
310{
311    fn len(&self) -> usize {
312        self.iter.len()
313    }
314}
315
316impl<'a, C> ValidComponentAmalgamationIter<'a, C> {
317    /// Filters by whether a component is definitely revoked.
318    ///
319    /// A value of None disables this filter.
320    ///
321    /// If you call this function multiple times on the same iterator,
322    /// only the last value is used.
323    ///
324    /// This filter only checks if the component is not revoked; it
325    /// does not check whether the certificate not revoked.
326    ///
327    /// This filter checks whether a component's revocation status is
328    /// [`RevocationStatus::Revoked`] or not.  The latter (i.e.,
329    /// `revoked(false)`) is equivalent to:
330    ///
331    /// ```rust
332    /// use sequoia_openpgp as openpgp;
333    /// # use openpgp::Result;
334    /// use openpgp::cert::prelude::*;
335    /// use openpgp::types::RevocationStatus;
336    /// use sequoia_openpgp::policy::StandardPolicy;
337    ///
338    /// # fn main() -> Result<()> {
339    /// let p = &StandardPolicy::new();
340    ///
341    /// # let (cert, _) =
342    /// #     CertBuilder::general_purpose(Some("alice@example.org"))
343    /// #     .generate()?;
344    /// # let timestamp = None;
345    /// let non_revoked_uas = cert
346    ///     .user_attributes()
347    ///     .with_policy(p, timestamp)
348    ///     .filter(|ca| {
349    ///         match ca.revocation_status() {
350    ///             RevocationStatus::Revoked(_) =>
351    ///                 // It's definitely revoked, skip it.
352    ///                 false,
353    ///             RevocationStatus::CouldBe(_) =>
354    ///                 // There is a designated revoker that we
355    ///                 // should check, but don't (or can't).  To
356    ///                 // avoid a denial of service arising from fake
357    ///                 // revocations, we assume that the component has not
358    ///                 // been revoked and return it.
359    ///                 true,
360    ///             RevocationStatus::NotAsFarAsWeKnow =>
361    ///                 // We have no evidence to suggest that the component
362    ///                 // is revoked.
363    ///                 true,
364    ///         }
365    ///     })
366    ///     .collect::<Vec<_>>();
367    /// #     Ok(())
368    /// # }
369    /// ```
370    ///
371    /// As the example shows, this filter is significantly less
372    /// flexible than using `ValidComponentAmalgamation::revocation_status`.
373    /// However, this filter implements a typical policy, and does not
374    /// preclude using `filter` to realize alternative policies.
375    ///
376    /// [`RevocationStatus::Revoked`]: crate::types::RevocationStatus::Revoked
377    pub fn revoked<T>(mut self, revoked: T) -> Self
378        where T: Into<Option<bool>>
379    {
380        self.revoked = revoked.into();
381        self
382    }
383}