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}