sequoia_openpgp/cert/amalgamation.rs
1//! Components, their associated signatures, and some useful methods.
2//!
3//! Whereas a [`ComponentBundle`] owns a `Component` and its
4//! associated [`Signature`]s, a [`ComponentAmalgamation`] references
5//! a `ComponentBundle` and its containing [`Cert`]. This additional
6//! context means that a `ComponentAmalgamation` can implement more of
7//! OpenPGP's high-level semantics than a `ComponentBundle` can. For
8//! instance, most of the information about a primary key, such as its
9//! capabilities, is on the primary User ID's binding signature. A
10//! `ComponentAmalgamation` can find the certificate's primary User
11//! ID; a `ComponentBundle` can't. Similarly, when looking up a
12//! subpacket, if it isn't present in the component's binding
13//! signature, then an OpenPGP implementation [is supposed to] consult
14//! the certificate's direct key signatures. A
15//! `ComponentAmalgamation` has access to this information; a
16//! `ComponentBundle` doesn't.
17//!
18//! Given the limitations of a `ComponentBundle`, it would seem more
19//! useful to just change it to include a reference to its containing
20//! certificate. That change would make `ComponentAmalgamation`s
21//! redundant. Unfortunately, this isn't possible, because it would
22//! result in a self-referential data structure, which Rust doesn't
23//! allow. To understand how this arises, consider a certificate `C`,
24//! which contains a `ComponentBundle` `B`. If `B` contains a
25//! reference to `C`, then `C` references itself, because `C` contains
26//! `B`!
27//!
28//! ```text
29//! Cert:[ Bundle:[ &Cert ] ]
30//! ^ |
31//! `------------'
32//! ```
33//!
34//! # Policy
35//!
36//! Although a `ComponentAmalgamation` contains the information
37//! necessary to realize high-level OpenPGP functionality, components
38//! can have multiple self signatures, and functions that consult the
39//! binding signature need to determine the best one to use. There
40//! are two main concerns here.
41//!
42//! First, we need to protect the user from forgeries. As attacks
43//! improve, cryptographic algorithms that were once considered secure
44//! now provide insufficient security margins. For instance, in 2007
45//! it was possible to find [MD5 collisions] using just a few seconds
46//! of computing time on a desktop computer. Sequoia provides a
47//! flexible mechanism, called [`Policy`] objects, that allow users to
48//! implement this type of filtering: before a self signature is used,
49//! a policy object is queried to determine whether the `Signature`
50//! should be rejected. If so, then it is skipped.
51//!
52//! Second, we need an algorithm to determine the most appropriate
53//! self signature. Obvious non-candidate self signatures are self
54//! signatures whose creation time is in the future. We don't assume
55//! that these self signatures are bad per se, but that they represent
56//! a policy that should go into effect some time in the future.
57//!
58//! We extend this idea of a self signature representing a policy for
59//! a certain period of time to all self signatures. In particular,
60//! Sequoia takes the view that *a binding signature represents a
61//! policy that is valid from its creation time until its expiry*.
62//! Thus, when considering what self signature to use, we need a
63//! reference time. Given the reference time, we then use the self
64//! signature that was in effect at that time, i.e., the most recent,
65//! non-expired, non-revoked self signature that was created at or
66//! prior to the reference time. In other words, we ignore self
67//! signatures created after the reference time. We take the position
68//! that if the certificate holder wants a new policy to apply to
69//! existing signatures, then the new self signature should be
70//! backdated, and existing self signatures revoked, if necessary.
71//!
72//! Consider evaluating a signature over a document. Sequoia's
73//! [streaming verifier] uses the signature's creation time as the
74//! reference time. Thus, if the signature was created on June 9th,
75//! 2011, then, when evaluating that signature, the streaming verifier
76//! uses a self signature that was live at that time, since that was
77//! the self signature that represented the signer's policy at the
78//! time the signature over the document was created.
79//!
80//! A consequence of this approach is that even if the self signature
81//! were considered expired at the time the signature was evaluated
82//! (e.g., "now"), this fact doesn't invalidate the signature. That
83//! is, a self signature's lifetime does not impact a signature's
84//! lifetime; a signature's lifetime is defined by its own creation
85//! time and expiry. Similarly, a key's lifetime is defined by its
86//! own creation time and expiry.
87//!
88//! This interpretation of lifetimes removes a major disadvantage that
89//! comes with fast rotation of subkeys: if an implementation binds
90//! the lifetime of signatures to the signing key, and the key
91//! expires, then old signatures are considered invalid. Consider a
92//! user who generates a new signature subkey each week, and sets it
93//! to expire after exactly one week. If we use the policy that the
94//! signature is only valid while the key *and* the self signature are
95//! live, then if someone checks the signature a week after receiving
96//! it, the signature will be considered invalid, because the key has
97//! expired. The practical result is that all old messages from this
98//! user will be considered invalid! Unfortunately, this will result
99//! in users becoming accustomed to seeing invalid signatures, and
100//! cause them to be less suspcious of them.
101//!
102//! Sequoia's low-level mechanisms support this interpretation of self
103//! signatures, but they do *not* enforce it. It is still possible to
104//! realize other policies using this low-level API.
105//!
106//! The possibility of abuse of this interpretation of signature
107//! lifetimes is limited. If a key has been compromised, then the
108//! right thing to do is to revoke it. Expiry doesn't help: the
109//! attacker can simply create self-signatures that say whatever she
110//! wants. Assuming the secret key material has not been compromised,
111//! then an attacker could still reuse a message that would otherwise
112//! be considered expired. However, the attacker will not be able to
113//! change the signature's creation time, so, assuming a mail context
114//! and MUAs that check that the time in the message's headers matches
115//! the signature's creation time, the mails will appear old.
116//! Further, this type of attack will be mitigated by the proposed
117//! "[Intended Recipients]" subpacket, which more tightly binds the
118//! message to its context.
119//!
120//! # [`ValidComponentAmalgamation`]
121//!
122//! Most operations need to query a `ComponentAmalgamation` for
123//! multiple pieces of information. Accidentally using a different
124//! `Policy` or a different reference time for one of the queries is
125//! easy, especially when the queries are spread across multiple
126//! functions. Further, using `None` for the reference time can
127//! result in subtle timing bugs as each function translates it to the
128//! current time on demand. In these cases, the correct approach
129//! would be for the user of the library to get the current time at
130//! the start of the operation. But, this is less convenient.
131//! Finally, passing a `Policy` and a reference time to most function
132//! calls clutters the code.
133//!
134//! To mitigate these issues, we have a separate data structure,
135//! `ValidComponentAmalgamation`, which combines a
136//! `ComponetAmalgamation`, a `Policy` and a reference time. It
137//! implements methods that require a `Policy` and reference time, but
138//! instead of requiring the caller to pass them in, it uses the ones
139//! embedded in the data structure. Further, when the
140//! `ValidComponentAmalgamation` constructor is passed `None` for the
141//! reference time, it eagerly stores the current time, and uses that
142//! for all operations. This approach elegantly solves all the
143//! aforementioned problems.
144//!
145//! [`ComponentBundle`]: super::bundle
146//! [`Signature`]: crate::packet::signature
147//! [`Cert`]: super
148//! [is supposed to]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.10
149//! [`std::iter::map`]: std::iter::Map
150//! [MD5 collisions]: https://en.wikipedia.org/wiki/MD5
151//! [`Policy`]: crate::policy::Policy
152//! [streaming verifier]: crate::parse::stream
153//! [Intended Recipients]: https://www.rfc-editor.org/rfc/rfc9580.html#intended-recipient-fingerprint
154//! [signature expirations]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.18
155use std::time;
156use std::time::{
157 Duration,
158 SystemTime,
159};
160use std::clone::Clone;
161use std::borrow::Borrow;
162
163use crate::{
164 cert::prelude::*,
165 crypto::{Signer, hash::Hash},
166 Error,
167 KeyHandle,
168 packet,
169 packet::{
170 Key,
171 Signature,
172 Unknown,
173 UserAttribute,
174 UserID,
175 key::{PrimaryRole, PublicParts},
176 },
177 Result,
178 policy::{
179 HashAlgoSecurity,
180 Policy,
181 },
182 seal,
183 types::{
184 AEADAlgorithm,
185 CompressionAlgorithm,
186 Features,
187 HashAlgorithm,
188 KeyServerPreferences,
189 RevocationKey,
190 RevocationStatus,
191 RevocationType,
192 SignatureType,
193 SymmetricAlgorithm,
194 },
195};
196
197mod iter;
198pub use iter::{
199 ComponentAmalgamationIter,
200 UnknownComponentAmalgamationIter,
201 UserAttributeAmalgamationIter,
202 UserIDAmalgamationIter,
203 ValidComponentAmalgamationIter,
204 ValidUserAttributeAmalgamationIter,
205 ValidUserIDAmalgamationIter,
206};
207
208pub mod key;
209
210/// Embeds a policy and a reference time in an amalgamation.
211///
212/// This is used to turn a [`ComponentAmalgamation`] into a
213/// [`ValidComponentAmalgamation`], and a [`KeyAmalgamation`] into a
214/// [`ValidKeyAmalgamation`].
215///
216/// A certificate or a component is considered valid if:
217///
218/// - It has a self signature that is live at time `t`.
219///
220/// - The policy considers it acceptable.
221///
222/// - The certificate is valid.
223///
224/// # Sealed trait
225///
226/// This trait is [sealed] and cannot be implemented for types outside this crate.
227/// Therefore it can be extended in a non-breaking way.
228/// If you want to implement the trait inside the crate
229/// you also need to implement the `seal::Sealed` marker trait.
230///
231/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
232///
233/// # Examples
234///
235/// ```
236/// # use sequoia_openpgp as openpgp;
237/// use openpgp::cert::prelude::*;
238/// use openpgp::policy::{Policy, StandardPolicy};
239///
240/// const POLICY: &dyn Policy = &StandardPolicy::new();
241///
242/// fn f(ua: UserIDAmalgamation) -> openpgp::Result<()> {
243/// let ua = ua.with_policy(POLICY, None)?;
244/// // ...
245/// # Ok(())
246/// }
247/// # fn main() -> openpgp::Result<()> {
248/// # let (cert, _) =
249/// # CertBuilder::general_purpose(Some("alice@example.org"))
250/// # .generate()?;
251/// # let ua = cert.userids().nth(0).expect("User IDs");
252/// # f(ua);
253/// # Ok(())
254/// # }
255/// ```
256///
257pub trait ValidateAmalgamation<'a, C: 'a>: seal::Sealed {
258 /// The type returned by `with_policy`.
259 ///
260 /// This is either a [`ValidComponentAmalgamation`] or
261 /// a [`ValidKeyAmalgamation`].
262 ///
263 type V;
264
265 /// Uses the specified `Policy` and reference time with the amalgamation.
266 ///
267 /// If `time` is `None`, the current time is used.
268 fn with_policy<T>(&self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
269 where T: Into<Option<time::SystemTime>>,
270 Self: Sized;
271}
272
273/// Applies a policy to an amalgamation.
274///
275/// This is an internal variant of `ValidateAmalgamation`, which
276/// allows validating a component for an otherwise invalid
277/// certificate. See `ValidComponentAmalgamation::primary` for an
278/// explanation.
279trait ValidateAmalgamationRelaxed<'a, C: 'a> {
280 /// The type returned by `with_policy`.
281 type V;
282
283 /// Changes the amalgamation's policy.
284 ///
285 /// If `time` is `None`, the current time is used.
286 ///
287 /// If `valid_cert` is `false`, then this does not also check
288 /// whether the certificate is valid; it only checks whether the
289 /// component is valid. Normally, this should be `true`. This
290 /// option is only expose to allow breaking an infinite recursion:
291 ///
292 /// - To check if a certificate is valid, we check if the
293 /// primary key is valid.
294 ///
295 /// - To check if the primary key is valid, we need the primary
296 /// key's self signature
297 ///
298 /// - To find the primary key's self signature, we need to find
299 /// the primary user id
300 ///
301 /// - To find the primary user id, we need to check if the user
302 /// id is valid.
303 ///
304 /// - To check if the user id is valid, we need to check that
305 /// the corresponding certificate is valid.
306 fn with_policy_relaxed<T>(&self, policy: &'a dyn Policy, time: T,
307 valid_cert: bool) -> Result<Self::V>
308 where T: Into<Option<time::SystemTime>>,
309 Self: Sized;
310}
311
312/// Methods for valid amalgamations.
313///
314/// The methods exposed by a `ValidComponentAmalgamation` are similar
315/// to those exposed by a `ComponentAmalgamation`, but the policy and
316/// reference time are included in the `ValidComponentAmalgamation`.
317/// This helps prevent using different policies or different reference
318/// times when using a component, which can easily happen when the
319/// checks span multiple functions.
320///
321/// # Sealed trait
322///
323/// This trait is [sealed] and cannot be implemented for types outside this crate.
324/// Therefore it can be extended in a non-breaking way.
325/// If you want to implement the trait inside the crate
326/// you also need to implement the `seal::Sealed` marker trait.
327///
328/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
329pub trait ValidAmalgamation<'a, C: 'a>: seal::Sealed
330{
331 /// Returns the valid amalgamation's associated certificate.
332 ///
333 /// # Examples
334 ///
335 /// ```
336 /// # use sequoia_openpgp as openpgp;
337 /// # use openpgp::cert::prelude::*;
338 /// # use openpgp::policy::StandardPolicy;
339 /// #
340 /// fn f(ua: &ValidUserIDAmalgamation) {
341 /// let vcert = ua.valid_cert();
342 /// // ...
343 /// }
344 /// # fn main() -> openpgp::Result<()> {
345 /// # let p = &StandardPolicy::new();
346 /// # let (cert, _) =
347 /// # CertBuilder::general_purpose(Some("alice@example.org"))
348 /// # .generate()?;
349 /// # let fpr = cert.fingerprint();
350 /// # let ua = cert.userids().nth(0).expect("User IDs");
351 /// # assert_eq!(ua.cert().fingerprint(), fpr);
352 /// # f(&ua.with_policy(p, None)?);
353 /// # Ok(())
354 /// # }
355 /// ```
356 fn valid_cert(&self) -> &ValidCert<'a>;
357
358 /// Returns the amalgamation's reference time.
359 ///
360 /// # Examples
361 ///
362 /// ```
363 /// # use std::time::{SystemTime, Duration, UNIX_EPOCH};
364 /// #
365 /// # use sequoia_openpgp as openpgp;
366 /// # use openpgp::cert::prelude::*;
367 /// # use openpgp::policy::StandardPolicy;
368 /// fn f(ua: &ValidUserIDAmalgamation) {
369 /// let t = ua.time();
370 /// // ...
371 /// }
372 /// # fn main() -> openpgp::Result<()> {
373 /// # let p = &StandardPolicy::new();
374 /// # let t = UNIX_EPOCH + Duration::from_secs(1554542220);
375 /// # let (cert, _) =
376 /// # CertBuilder::general_purpose(Some("alice@example.org"))
377 /// # .set_creation_time(t)
378 /// # .generate()?;
379 /// # let ua = cert.userids().nth(0).expect("User IDs");
380 /// # let ua = ua.with_policy(p, t)?;
381 /// # assert_eq!(t, ua.time());
382 /// # f(&ua);
383 /// # Ok(())
384 /// # }
385 /// ```
386 fn time(&self) -> SystemTime;
387
388 /// Returns the amalgamation's policy.
389 ///
390 /// # Examples
391 ///
392 /// ```
393 /// # use sequoia_openpgp as openpgp;
394 /// # use openpgp::cert::prelude::*;
395 /// # use openpgp::policy::{Policy, StandardPolicy};
396 /// #
397 /// fn f(ua: &ValidUserIDAmalgamation) {
398 /// let policy = ua.policy();
399 /// // ...
400 /// }
401 /// # fn main() -> openpgp::Result<()> {
402 /// # let p: &dyn Policy = &StandardPolicy::new();
403 /// # let (cert, _) =
404 /// # CertBuilder::general_purpose(Some("alice@example.org"))
405 /// # .generate()?;
406 /// # let ua = cert.userids().nth(0).expect("User IDs");
407 /// # let ua = ua.with_policy(p, None)?;
408 /// # assert!(std::ptr::eq(p, ua.policy()));
409 /// # f(&ua);
410 /// # Ok(())
411 /// # }
412 /// ```
413 fn policy(&self) -> &'a dyn Policy;
414
415 /// Returns the component's binding signature as of the reference time.
416 ///
417 /// # Examples
418 ///
419 /// ```
420 /// # use sequoia_openpgp as openpgp;
421 /// # use openpgp::cert::prelude::*;
422 /// # use openpgp::policy::{Policy, StandardPolicy};
423 /// #
424 /// fn f(ua: &ValidUserIDAmalgamation) {
425 /// let sig = ua.binding_signature();
426 /// // ...
427 /// }
428 /// # fn main() -> openpgp::Result<()> {
429 /// # let p: &dyn Policy = &StandardPolicy::new();
430 /// # let (cert, _) =
431 /// # CertBuilder::general_purpose(Some("alice@example.org"))
432 /// # .generate()?;
433 /// # let ua = cert.userids().nth(0).expect("User IDs");
434 /// # let ua = ua.with_policy(p, None)?;
435 /// # f(&ua);
436 /// # Ok(())
437 /// # }
438 /// ```
439 fn binding_signature(&self) -> &'a Signature;
440
441 /// Returns the certificate's direct key signature as of the
442 /// reference time, if any.
443 ///
444 /// Subpackets on direct key signatures apply to all components of
445 /// the certificate, cf. [Section 5.2.3.10 of RFC 9580].
446 ///
447 /// [Section 5.2.3.10 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.10
448 ///
449 /// # Examples
450 ///
451 /// ```
452 /// # use sequoia_openpgp as openpgp;
453 /// # use openpgp::cert::prelude::*;
454 /// # use openpgp::policy::{Policy, StandardPolicy};
455 /// #
456 /// fn f(ua: &ValidUserIDAmalgamation) {
457 /// let sig = ua.direct_key_signature();
458 /// // ...
459 /// }
460 /// # fn main() -> openpgp::Result<()> {
461 /// # let p: &dyn Policy = &StandardPolicy::new();
462 /// # let (cert, _) =
463 /// # CertBuilder::general_purpose(Some("alice@example.org"))
464 /// # .generate()?;
465 /// # let cert = cert.with_policy(p, None)?;
466 /// # let ua = cert.userids().nth(0).expect("User IDs");
467 /// # assert!(std::ptr::eq(ua.direct_key_signature().unwrap(),
468 /// # cert.direct_key_signature().unwrap()));
469 /// # f(&ua);
470 /// # Ok(())
471 /// # }
472 /// ```
473 fn direct_key_signature(&self) -> Result<&'a Signature> {
474 self.valid_cert().cert().primary_key()
475 .binding_signature(self.policy(), self.time())
476 }
477
478 /// Returns the component's revocation status as of the amalgamation's
479 /// reference time.
480 ///
481 /// This does *not* check whether the certificate has been
482 /// revoked. For that, use `Cert::revocation_status()`.
483 ///
484 /// Note, as per [Section 5.2.3.31 of RFC 9580], a key is considered to be revoked at
485 /// some time if there were no soft revocations created as of that
486 /// time, and no hard revocations:
487 ///
488 /// > If a key has been revoked because of a compromise, all signatures
489 /// > created by that key are suspect. However, if it was merely
490 /// > superseded or retired, old signatures are still valid.
491 ///
492 /// [Section 5.2.3.31 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.31
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// # use sequoia_openpgp as openpgp;
498 /// use openpgp::cert::prelude::*;
499 /// # use openpgp::policy::StandardPolicy;
500 /// use openpgp::types::RevocationStatus;
501 ///
502 /// # fn main() -> openpgp::Result<()> {
503 /// # let p = &StandardPolicy::new();
504 /// # let (cert, _) =
505 /// # CertBuilder::general_purpose(Some("alice@example.org"))
506 /// # .generate()?;
507 /// # let cert = cert.with_policy(p, None)?;
508 /// # let ua = cert.userids().nth(0).expect("User IDs");
509 /// match ua.revocation_status() {
510 /// RevocationStatus::Revoked(revs) => {
511 /// // The certificate holder revoked the User ID.
512 /// # unreachable!();
513 /// }
514 /// RevocationStatus::CouldBe(revs) => {
515 /// // There are third-party revocations. You still need
516 /// // to check that they are valid (this is necessary,
517 /// // because without the Certificates are not normally
518 /// // available to Sequoia).
519 /// # unreachable!();
520 /// }
521 /// RevocationStatus::NotAsFarAsWeKnow => {
522 /// // We have no evidence that the User ID is revoked.
523 /// }
524 /// }
525 /// # Ok(())
526 /// # }
527 /// ```
528 fn revocation_status(&self) -> RevocationStatus<'a>;
529
530 /// Returns a list of any designated revokers for this component.
531 ///
532 /// This function returns the designated revokers listed on the
533 /// components' binding signatures and the certificate's direct
534 /// key signatures.
535 ///
536 /// Note: the returned list is deduplicated.
537 ///
538 /// # Examples
539 ///
540 /// ```
541 /// use sequoia_openpgp as openpgp;
542 /// # use openpgp::Result;
543 /// use openpgp::cert::prelude::*;
544 /// use openpgp::policy::StandardPolicy;
545 /// use openpgp::types::RevocationKey;
546 ///
547 /// # fn main() -> Result<()> {
548 /// let p = &StandardPolicy::new();
549 ///
550 /// let (alice, _) =
551 /// CertBuilder::general_purpose(Some("alice@example.org"))
552 /// .generate()?;
553 /// // Make Alice a designated revoker for Bob.
554 /// let (bob, _) =
555 /// CertBuilder::general_purpose(Some("bob@example.org"))
556 /// .set_revocation_keys(vec![(&alice).into()])
557 /// .generate()?;
558 ///
559 /// // Make sure Alice is listed as a designated revoker for Bob's
560 /// // primary user id.
561 /// assert_eq!(bob.with_policy(p, None)?.primary_userid()?
562 /// .revocation_keys().collect::<Vec<&RevocationKey>>(),
563 /// vec![&(&alice).into()]);
564 ///
565 /// // Make sure Alice is listed as a designated revoker for Bob's
566 /// // encryption subkey.
567 /// assert_eq!(bob.with_policy(p, None)?
568 /// .keys().for_transport_encryption().next().unwrap()
569 /// .revocation_keys().collect::<Vec<&RevocationKey>>(),
570 /// vec![&(&alice).into()]);
571 /// # Ok(()) }
572 /// ```
573 fn revocation_keys(&self)
574 -> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>;
575}
576
577#[test]
578fn valid_amalgamation_is_dyn_compatible() {
579 let _t: Option<Box<dyn ValidAmalgamation<()>>> = None;
580}
581
582/// Locates information on the active binding signature or direct key
583/// signature.
584///
585/// # Sealed trait
586///
587/// This trait is [sealed] and cannot be implemented for types outside
588/// this crate. Therefore it can be extended in a non-breaking way.
589/// If you want to implement the trait inside the crate you also need
590/// to implement the `seal::Sealed` marker trait.
591///
592/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
593pub trait ValidBindingSignature<'a, C: 'a>: ValidAmalgamation<'a, C> + seal::Sealed
594{
595 /// Maps the given function over binding and direct key signature.
596 ///
597 /// Makes `f` consider both the binding signature and the direct
598 /// key signature. Information in the binding signature takes
599 /// precedence over the direct key signature. See also [Section
600 /// 5.2.3.10 of RFC 9580].
601 ///
602 /// [Section 5.2.3.10 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.10
603 fn map<F: Fn(&'a Signature) -> Option<T>, T>(&self, f: F) -> Option<T> {
604 f(self.binding_signature())
605 .or_else(|| self.direct_key_signature().ok().and_then(f))
606 }
607}
608
609/// A certificate component, its associated data, and useful methods.
610///
611/// [`Cert::userids`], [`ValidCert::primary_userid`], [`Cert::user_attributes`], and
612/// [`Cert::unknowns`] return `ComponentAmalgamation`s.
613///
614/// `ComponentAmalgamation` implements [`ValidateAmalgamation`], which
615/// allows you to turn a `ComponentAmalgamation` into a
616/// [`ValidComponentAmalgamation`] using
617/// [`ComponentAmalgamation::with_policy`].
618///
619/// [See the module's documentation] for more details.
620///
621/// # Examples
622///
623/// ```
624/// # use sequoia_openpgp as openpgp;
625/// # use openpgp::cert::prelude::*;
626/// # use openpgp::policy::StandardPolicy;
627/// #
628/// # fn main() -> openpgp::Result<()> {
629/// # let p = &StandardPolicy::new();
630/// # let (cert, _) =
631/// # CertBuilder::general_purpose(Some("alice@example.org"))
632/// # .generate()?;
633/// # let fpr = cert.fingerprint();
634/// // Iterate over all User IDs.
635/// for ua in cert.userids() {
636/// // ua is a `ComponentAmalgamation`, specifically, a `UserIDAmalgamation`.
637/// }
638/// # Ok(())
639/// # }
640/// ```
641///
642/// [`Cert`]: super::Cert
643/// [`Cert::userids`]: super::Cert::userids()
644/// [`ValidCert::primary_userid`]: super::ValidCert::primary_userid()
645/// [`Cert::user_attributes`]: super::Cert::user_attributes()
646/// [`Cert::unknowns`]: super::Cert::unknowns()
647/// [`ComponentAmalgamation::with_policy`]: ValidateAmalgamation::with_policy()
648/// [See the module's documentation]: self
649#[derive(Debug, PartialEq)]
650pub struct ComponentAmalgamation<'a, C> {
651 cert: &'a Cert,
652 bundle: &'a ComponentBundle<C>,
653}
654assert_send_and_sync!(ComponentAmalgamation<'_, C> where C);
655
656/// A User ID and its associated data.
657///
658/// A specialized version of [`ComponentAmalgamation`].
659///
660pub type UserIDAmalgamation<'a> = ComponentAmalgamation<'a, UserID>;
661
662/// A User Attribute and its associated data.
663///
664/// A specialized version of [`ComponentAmalgamation`].
665///
666pub type UserAttributeAmalgamation<'a>
667 = ComponentAmalgamation<'a, UserAttribute>;
668
669/// An Unknown component and its associated data.
670///
671/// A specialized version of [`ComponentAmalgamation`].
672///
673pub type UnknownComponentAmalgamation<'a>
674 = ComponentAmalgamation<'a, Unknown>;
675
676// derive(Clone) doesn't work with generic parameters that don't
677// implement clone. But, we don't need to require that C implements
678// Clone, because we're not cloning C, just the reference.
679//
680// See: https://github.com/rust-lang/rust/issues/26925
681impl<'a, C> Clone for ComponentAmalgamation<'a, C> {
682 fn clone(&self) -> Self {
683 Self {
684 cert: self.cert,
685 bundle: self.bundle,
686 }
687 }
688}
689
690impl<'a, C> ComponentAmalgamation<'a, C> {
691 /// Creates a new amalgamation.
692 pub(crate) fn new(cert: &'a Cert, bundle: &'a ComponentBundle<C>) -> Self
693 {
694 Self {
695 cert,
696 bundle,
697 }
698 }
699
700 /// Returns the amalgamations's associated certificate.
701 ///
702 /// ```
703 /// # use sequoia_openpgp as openpgp;
704 /// # use openpgp::cert::prelude::*;
705 /// #
706 /// # fn main() -> openpgp::Result<()> {
707 /// # let (cert, _) =
708 /// # CertBuilder::general_purpose(Some("alice@example.org"))
709 /// # .generate()?;
710 /// for u in cert.userids() {
711 /// // It's not only an identical `Cert`, it's the same one.
712 /// assert!(std::ptr::eq(u.cert(), &cert));
713 /// }
714 /// # Ok(()) }
715 /// ```
716 pub fn cert(&self) -> &'a Cert {
717 self.cert
718 }
719
720 /// Returns the active binding signature at time `t`.
721 ///
722 /// The active binding signature is the most recent, non-revoked
723 /// self-signature that is valid according to the `policy` and
724 /// alive at time `t` (`creation time <= t`, `t < expiry`). If
725 /// there are multiple such signatures then the signatures are
726 /// ordered by their MPIs interpreted as byte strings.
727 ///
728 /// # Examples
729 ///
730 /// ```
731 /// # use sequoia_openpgp as openpgp;
732 /// # use openpgp::cert::prelude::*;
733 /// use openpgp::policy::StandardPolicy;
734 /// #
735 /// # fn main() -> openpgp::Result<()> {
736 /// let p = &StandardPolicy::new();
737 ///
738 /// # let (cert, _) =
739 /// # CertBuilder::general_purpose(Some("alice@example.org"))
740 /// # .generate()?;
741 /// // Display information about each User ID's current active
742 /// // binding signature (the `time` parameter is `None`), if any.
743 /// for ua in cert.userids() {
744 /// eprintln!("{:?}", ua.binding_signature(p, None));
745 /// }
746 /// # Ok(()) }
747 /// ```
748 pub fn binding_signature<T>(&self, policy: &dyn Policy, time: T)
749 -> Result<&'a Signature>
750 where T: Into<Option<time::SystemTime>>
751 {
752 let time = time.into().unwrap_or_else(crate::now);
753 self.bundle().binding_signature(policy, time)
754 }
755
756 /// Returns this amalgamation's bundle.
757 ///
758 /// # Examples
759 ///
760 /// ```
761 /// # fn main() -> sequoia_openpgp::Result<()> {
762 /// # use sequoia_openpgp as openpgp;
763 /// use openpgp::cert::prelude::*;
764 /// use openpgp::packet::prelude::*;
765 ///
766 /// # let (cert, _) = CertBuilder::new()
767 /// # .add_userid("Alice")
768 /// # .add_signing_subkey()
769 /// # .add_transport_encryption_subkey()
770 /// # .generate()?;
771 /// cert.userids()
772 /// .map(|ua| ua.bundle())
773 /// .collect::<Vec<&ComponentBundle<_>>>();
774 /// # Ok(()) }
775 /// ```
776 pub fn bundle(&self) -> &'a ComponentBundle<C> {
777 self.bundle
778 }
779
780 /// Returns this amalgamation's component.
781 ///
782 /// # Examples
783 ///
784 /// ```
785 /// # use sequoia_openpgp as openpgp;
786 /// # use openpgp::cert::prelude::*;
787 /// #
788 /// # fn main() -> openpgp::Result<()> {
789 /// # let (cert, _) =
790 /// # CertBuilder::general_purpose(Some("alice@example.org"))
791 /// # .generate()?;
792 /// // Display some information about any unknown components.
793 /// for u in cert.unknowns() {
794 /// eprintln!(" - {:?}", u.component());
795 /// }
796 /// # Ok(()) }
797 /// ```
798 pub fn component(&self) -> &'a C {
799 self.bundle().component()
800 }
801
802 /// Returns the component's self-signatures.
803 ///
804 /// The signatures are validated, and they are sorted by their
805 /// creation time, most recent first.
806 ///
807 /// # Examples
808 ///
809 /// ```
810 /// # use sequoia_openpgp as openpgp;
811 /// # use openpgp::cert::prelude::*;
812 /// use openpgp::policy::StandardPolicy;
813 /// #
814 /// # fn main() -> openpgp::Result<()> {
815 /// let p = &StandardPolicy::new();
816 ///
817 /// # let (cert, _) =
818 /// # CertBuilder::general_purpose(Some("alice@example.org"))
819 /// # .generate()?;
820 /// for (i, ka) in cert.keys().enumerate() {
821 /// eprintln!("Key #{} ({}) has {:?} self signatures",
822 /// i, ka.key().fingerprint(),
823 /// ka.self_signatures().count());
824 /// }
825 /// # Ok(()) }
826 /// ```
827 pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
828 self.bundle().self_signatures()
829 }
830
831 /// Returns the component's third-party certifications.
832 ///
833 /// The signatures are *not* validated. They are sorted by their
834 /// creation time, most recent first.
835 ///
836 /// # Examples
837 ///
838 /// ```
839 /// # use sequoia_openpgp as openpgp;
840 /// # use openpgp::cert::prelude::*;
841 /// use openpgp::policy::StandardPolicy;
842 /// #
843 /// # fn main() -> openpgp::Result<()> {
844 /// let p = &StandardPolicy::new();
845 ///
846 /// # let (cert, _) =
847 /// # CertBuilder::general_purpose(Some("alice@example.org"))
848 /// # .generate()?;
849 /// for ua in cert.userids() {
850 /// eprintln!("User ID {} has {:?} unverified, third-party certifications",
851 /// String::from_utf8_lossy(ua.userid().value()),
852 /// ua.certifications().count());
853 /// }
854 /// # Ok(()) }
855 /// ```
856 pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
857 self.bundle().certifications()
858 }
859
860 /// Returns third-party certifications that appear to issued by
861 /// any of the specified keys.
862 ///
863 /// A certification is returned if one of the provided key handles
864 /// matches an [Issuer subpacket] or [Issuer Fingerprint
865 /// subpacket] in the certification.
866 ///
867 /// [Issuer subpacket]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.12
868 /// [Issuer Fingerprint subpacket]: https://www.rfc-editor.org/rfc/rfc9580.html#name-intended-recipient-fingerpr
869 ///
870 /// This function does not check that a certification is valid.
871 /// It can't. To check that a certification was actually issued
872 /// by a specific key, we also need a policy and the public key,
873 /// which we don't have. To only get valid certifications, use
874 /// [`UserIDAmalgamation::valid_certifications_by_key`] or
875 /// [`UserIDAmalgamation::active_certifications_by_key`] instead
876 /// of this function.
877 pub fn certifications_by_key<'b>(&'b self, issuers: &'b [ KeyHandle ])
878 -> impl Iterator<Item=&'a Signature> + Send + Sync + 'b
879 {
880 self.certifications().filter(|certification| {
881 certification.get_issuers().into_iter().any(|certification_issuer| {
882 issuers.iter().any(|issuer| {
883 certification_issuer.aliases(issuer)
884 })
885 })
886 })
887 }
888
889 /// Returns the component's revocations that were issued by the
890 /// certificate holder.
891 ///
892 /// The revocations are validated, and they are sorted by their
893 /// creation time, most recent first.
894 ///
895 /// # Examples
896 ///
897 /// ```
898 /// # use sequoia_openpgp as openpgp;
899 /// # use openpgp::cert::prelude::*;
900 /// use openpgp::policy::StandardPolicy;
901 /// #
902 /// # fn main() -> openpgp::Result<()> {
903 /// let p = &StandardPolicy::new();
904 ///
905 /// # let (cert, _) =
906 /// # CertBuilder::general_purpose(Some("alice@example.org"))
907 /// # .generate()?;
908 /// for u in cert.userids() {
909 /// eprintln!("User ID {} has {:?} revocation certificates.",
910 /// String::from_utf8_lossy(u.userid().value()),
911 /// u.self_revocations().count());
912 /// }
913 /// # Ok(()) }
914 /// ```
915 pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
916 self.bundle().self_revocations()
917 }
918
919 /// Returns the component's revocations that were issued by other
920 /// certificates.
921 ///
922 /// The revocations are *not* validated. They are sorted by their
923 /// creation time, most recent first.
924 ///
925 /// # Examples
926 ///
927 /// ```
928 /// # use sequoia_openpgp as openpgp;
929 /// # use openpgp::cert::prelude::*;
930 /// use openpgp::policy::StandardPolicy;
931 /// #
932 /// # fn main() -> openpgp::Result<()> {
933 /// let p = &StandardPolicy::new();
934 ///
935 /// # let (cert, _) =
936 /// # CertBuilder::general_purpose(Some("alice@example.org"))
937 /// # .generate()?;
938 /// for u in cert.userids() {
939 /// eprintln!("User ID {} has {:?} unverified, third-party revocation certificates.",
940 /// String::from_utf8_lossy(u.userid().value()),
941 /// u.other_revocations().count());
942 /// }
943 /// # Ok(()) }
944 /// ```
945 pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync {
946 self.bundle().other_revocations()
947 }
948
949 /// Returns all the component's Certification Approval Key
950 /// Signatures.
951 ///
952 /// This feature is [experimental](crate#experimental-features).
953 ///
954 /// The signatures are validated, and they are sorted by their
955 /// creation time, most recent first.
956 ///
957 /// A certificate owner can use Certification Approval Key
958 /// Signatures to approve of third party certifications.
959 /// Currently, only userid and user attribute certifications can
960 /// be approved of. See [Approved Certifications subpacket] for
961 /// details.
962 ///
963 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
964 ///
965 /// # Examples
966 ///
967 /// ```
968 /// # use sequoia_openpgp as openpgp;
969 /// # fn main() -> openpgp::Result<()> {
970 /// # use openpgp::cert::prelude::*;
971 /// use openpgp::policy::StandardPolicy;
972 /// let p = &StandardPolicy::new();
973 ///
974 /// # let (cert, _) =
975 /// # CertBuilder::general_purpose(Some("alice@example.org"))
976 /// # .generate()?;
977 /// for (i, uid) in cert.userids().enumerate() {
978 /// eprintln!("UserID #{} ({:?}) has {:?} certification approval key signatures",
979 /// i, uid.userid().email(),
980 /// uid.approvals().count());
981 /// }
982 /// # Ok(()) }
983 /// ```
984 pub fn approvals(&self)
985 -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a
986 {
987 self.bundle().approvals()
988 }
989
990 /// Returns all the component's signatures.
991 ///
992 /// Only the self-signatures are validated. The signatures are
993 /// sorted first by type, then by creation time. The self
994 /// revocations come first, then the self signatures,
995 /// then any certification approval key signatures,
996 /// certifications, and third-party revocations coming last. This
997 /// function may return additional types of signatures that could
998 /// be associated to this component.
999 ///
1000 /// # Examples
1001 ///
1002 /// ```
1003 /// # use sequoia_openpgp as openpgp;
1004 /// # use openpgp::cert::prelude::*;
1005 /// use openpgp::policy::StandardPolicy;
1006 /// #
1007 /// # fn main() -> openpgp::Result<()> {
1008 /// let p = &StandardPolicy::new();
1009 ///
1010 /// # let (cert, _) =
1011 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1012 /// # .generate()?;
1013 /// for (i, ka) in cert.keys().enumerate() {
1014 /// eprintln!("Key #{} ({}) has {:?} signatures",
1015 /// i, ka.key().fingerprint(),
1016 /// ka.signatures().count());
1017 /// }
1018 /// # Ok(()) }
1019 /// ```
1020 pub fn signatures(&self)
1021 -> impl Iterator<Item = &'a Signature> + Send + Sync {
1022 self.bundle().signatures()
1023 }
1024
1025 // Used to implement
1026 // [`UserIDAmalgamation::valid_certifications_by_key`],
1027 // [`KeyAmalgamation::valid_certifications_by_key`],
1028 // [`UserIDAmalgamation::active_certifications_by_key`], and
1029 // [`KeyAmalgamation::active_certifications_by_key`].
1030 fn valid_certifications_by_key_<'b, F>(
1031 &self,
1032 policy: &'a dyn Policy,
1033 reference_time: Option<time::SystemTime>,
1034 issuer: &'a packet::Key<packet::key::PublicParts,
1035 packet::key::UnspecifiedRole>,
1036 only_active: bool,
1037 certifications: impl Iterator<Item=&'b Signature> + Send + Sync,
1038 verify_certification: F)
1039 -> impl Iterator<Item=&'b Signature> + Send + Sync
1040 where
1041 F: Fn(&Signature) -> Result<()>
1042 {
1043 let reference_time = reference_time.unwrap_or_else(crate::now);
1044 let issuer_handle = issuer.key_handle();
1045 let issuer_handle = &issuer_handle;
1046
1047 let mut certifications: Vec<(&Signature, _)> = certifications
1048 .filter_map(|certification| {
1049 // Extract the signature's creation time. Ignore
1050 // certifications without a creation time: those are
1051 // malformed.
1052 certification
1053 .signature_creation_time()
1054 .map(|ct| (certification, ct))
1055 })
1056 .filter(|(certification, _ct)| {
1057 // Filter out certifications that definitely aren't
1058 // from `issuer`.
1059 certification.get_issuers().into_iter().any(|sig_issuer| {
1060 sig_issuer.aliases(issuer_handle)
1061 })
1062 })
1063 .map(|(certification, ct)| {
1064 let hard = if matches!(certification.typ(),
1065 SignatureType::KeyRevocation
1066 | SignatureType::SubkeyRevocation
1067 | SignatureType::CertificationRevocation)
1068 {
1069 certification.reason_for_revocation()
1070 .map(|(reason, _text)| {
1071 reason.revocation_type() == RevocationType::Hard
1072 })
1073 // Interpret an unspecified reason as a hard
1074 // revocation.
1075 .unwrap_or(true)
1076 } else {
1077 false
1078 };
1079
1080 (certification, ct, hard)
1081 })
1082 .filter(|(_certification, ct, hard)| {
1083 // Skip certifications created after the reference
1084 // time, unless they are hard revocations.
1085 *ct <= reference_time || *hard
1086 })
1087 .filter(|(certification, ct, hard)| {
1088 // Check that the certification is not expired as of
1089 // the reference time.
1090 if *hard {
1091 // Hard revocations don't expire.
1092 true
1093 } else if let Some(validity)
1094 = certification.signature_validity_period()
1095 {
1096 if validity == Duration::new(0, 0) {
1097 // "If this is not present or has a value of
1098 // zero, it never expires."
1099 //
1100 // https://www.rfc-editor.org/rfc/rfc9580.html#name-key-expiration-time
1101 true
1102 } else {
1103 // "the number of seconds after the signature
1104 // creation time that the signature expires"
1105 //
1106 // Assume validity = 1 second, then:
1107 //
1108 // expiry time reference time status
1109 // ----------- -------------- ------
1110 // > ct live
1111 // ct + 1 = ct + 1 expired
1112 // < ct + 2 expired
1113 *ct + validity > reference_time
1114 }
1115 } else {
1116 true
1117 }
1118 })
1119 .filter(|(_certification, ct, hard)| {
1120 // Make sure the certification was created after the
1121 // certificate, unless they are hard revocations.
1122 self.cert.primary_key().key().creation_time() <= *ct || *hard
1123 })
1124 .filter(|(certification, _ct, _hard)| {
1125 // Make sure the certification conforms to the policy.
1126 policy
1127 .signature(certification,
1128 HashAlgoSecurity::CollisionResistance)
1129 .is_ok()
1130 })
1131 .map(|(certification, ct, _hard)| (certification, ct))
1132 .collect();
1133
1134 // Sort the certifications by creation time so that the newest
1135 // certifications come first.
1136 certifications.sort_unstable_by(|(_, a), (_, b)| {
1137 a.cmp(b).reverse()
1138 });
1139
1140 // Check that the issuer actually made the signatures, and
1141 // collect the most recent certifications.
1142 let mut valid = Vec::new();
1143 for (certification, ct) in certifications.into_iter() {
1144 if only_active {
1145 if let Some((_active, active_ct)) = valid.get(0) {
1146 if *active_ct != ct {
1147 // This certification is further in the past.
1148 // We're done.
1149 break;
1150 }
1151 }
1152 }
1153
1154 if let Ok(()) = verify_certification(certification) {
1155 valid.push((certification, ct));
1156 }
1157 }
1158
1159 valid.into_iter()
1160 .map(|(certification, _creation_time)| certification)
1161 .collect::<Vec<&Signature>>()
1162 .into_iter()
1163 }
1164}
1165
1166macro_rules! impl_with_policy {
1167 ($func:ident, $value:ident $(, $arg:ident: $type:ty )*) => {
1168 fn $func<T>(&self, policy: &'a dyn Policy, time: T, $($arg: $type, )*)
1169 -> Result<Self::V>
1170 where T: Into<Option<time::SystemTime>>,
1171 Self: Sized
1172 {
1173 let time = time.into().unwrap_or_else(crate::now);
1174
1175 if $value {
1176 self.cert.with_policy(policy, time)?;
1177 }
1178
1179 let binding_signature = self.binding_signature(policy, time)?;
1180 let cert = self.cert;
1181 // We can't do `Cert::with_policy` as that would
1182 // result in infinite recursion. But at this point,
1183 // we know the certificate is valid (unless the caller
1184 // doesn't care).
1185 Ok(ValidComponentAmalgamation {
1186 ca: self.clone(),
1187 cert: ValidCert {
1188 cert,
1189 policy,
1190 time,
1191 },
1192 binding_signature,
1193 })
1194 }
1195 }
1196}
1197
1198impl<'a, C> seal::Sealed for ComponentAmalgamation<'a, C> {}
1199impl<'a, C> ValidateAmalgamation<'a, C> for ComponentAmalgamation<'a, C> {
1200 type V = ValidComponentAmalgamation<'a, C>;
1201
1202 impl_with_policy!(with_policy, true);
1203}
1204
1205impl<'a, C> ValidateAmalgamationRelaxed<'a, C> for ComponentAmalgamation<'a, C> {
1206 type V = ValidComponentAmalgamation<'a, C>;
1207
1208 impl_with_policy!(with_policy_relaxed, valid_cert, valid_cert: bool);
1209}
1210
1211impl<'a> UserIDAmalgamation<'a> {
1212 /// Returns a reference to the User ID.
1213 ///
1214 /// This is just a type-specific alias for
1215 /// [`ComponentAmalgamation::component`].
1216 ///
1217 /// # Examples
1218 ///
1219 /// ```
1220 /// # use sequoia_openpgp as openpgp;
1221 /// # use openpgp::cert::prelude::*;
1222 /// #
1223 /// # fn main() -> openpgp::Result<()> {
1224 /// # let (cert, _) =
1225 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1226 /// # .generate()?;
1227 /// // Display some information about the User IDs.
1228 /// for ua in cert.userids() {
1229 /// eprintln!(" - {:?}", ua.userid());
1230 /// }
1231 /// # Ok(()) }
1232 /// ```
1233 pub fn userid(&self) -> &'a UserID {
1234 self.component()
1235 }
1236
1237 /// Returns the User ID's revocation status at time `t`.<a
1238 /// name="userid_revocation_status"></a>
1239 ///
1240 /// <!-- Why we have the above anchor:
1241 /// https://github.com/rust-lang/rust/issues/71912 -->
1242 ///
1243 /// A User ID is revoked at time `t` if:
1244 ///
1245 /// - There is a live revocation at time `t` that is newer than
1246 /// all live self signatures at time `t`.
1247 ///
1248 /// Note: Certs and subkeys have different criteria from User IDs
1249 /// and User Attributes.
1250 ///
1251 /// Note: this only returns whether this User ID is revoked; it
1252 /// does not imply anything about the Cert or other components.
1253 //
1254 /// # Examples
1255 ///
1256 /// ```
1257 /// # use sequoia_openpgp as openpgp;
1258 /// # use openpgp::cert::prelude::*;
1259 /// use openpgp::policy::StandardPolicy;
1260 /// #
1261 /// # fn main() -> openpgp::Result<()> {
1262 /// let p = &StandardPolicy::new();
1263 ///
1264 /// # let (cert, _) =
1265 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1266 /// # .generate()?;
1267 /// // Display the User IDs' revocation status.
1268 /// for ua in cert.userids() {
1269 /// eprintln!(" Revocation status of {}: {:?}",
1270 /// String::from_utf8_lossy(ua.userid().value()),
1271 /// ua.revocation_status(p, None));
1272 /// }
1273 /// # Ok(()) }
1274 /// ```
1275 pub fn revocation_status<T>(&self, policy: &dyn Policy, t: T)
1276 -> RevocationStatus
1277 where
1278 T: Into<Option<time::SystemTime>>,
1279 {
1280 let t = t.into();
1281 self.bundle().revocation_status(policy, t)
1282 }
1283
1284 /// Returns the third-party certifications issued by the specified
1285 /// key, and valid at the specified time.
1286 ///
1287 /// This function returns the certifications issued by the
1288 /// specified key. Specifically, it returns a certification if:
1289 ///
1290 /// - it is well-formed,
1291 /// - it is live with respect to the reference time,
1292 /// - it conforms to the policy, and
1293 /// - the signature is cryptographically valid.
1294 ///
1295 /// This method is implemented on a [`UserIDAmalgamation`] and not
1296 /// a [`ValidUserIDAmalgamation`], because a third-party
1297 /// certification does not require the user ID to be self-signed.
1298 ///
1299 /// # Examples
1300 ///
1301 /// Alice has certified that a certificate belongs to Bob on two
1302 /// occasions. Whereas
1303 /// [`UserIDAmalgamation::valid_certifications_by_key`] returns
1304 /// both certifications,
1305 /// [`UserIDAmalgamation::active_certifications_by_key`] only
1306 /// returns the most recent certification.
1307 ///
1308 /// ```rust
1309 /// use sequoia_openpgp as openpgp;
1310 /// use openpgp::cert::prelude::*;
1311 /// # use openpgp::packet::signature::SignatureBuilder;
1312 /// # use openpgp::packet::UserID;
1313 /// use openpgp::policy::StandardPolicy;
1314 /// # use openpgp::types::SignatureType;
1315 ///
1316 /// const P: &StandardPolicy = &StandardPolicy::new();
1317 ///
1318 /// # fn main() -> openpgp::Result<()> {
1319 /// # let epoch = std::time::SystemTime::now()
1320 /// # - std::time::Duration::new(100, 0);
1321 /// # let t0 = epoch;
1322 /// #
1323 /// # let (alice, _) = CertBuilder::new()
1324 /// # .set_creation_time(t0)
1325 /// # .add_userid("<alice@example.org>")
1326 /// # .generate()
1327 /// # .unwrap();
1328 /// let alice: Cert = // ...
1329 /// # alice;
1330 /// #
1331 /// # let bob_userid = "<bob@example.org>";
1332 /// # let (bob, _) = CertBuilder::new()
1333 /// # .set_creation_time(t0)
1334 /// # .add_userid(bob_userid)
1335 /// # .generate()
1336 /// # .unwrap();
1337 /// let bob: Cert = // ...
1338 /// # bob;
1339 ///
1340 /// # // Alice has not certified Bob's User ID.
1341 /// # let ua = bob.userids().next().expect("have a user id");
1342 /// # assert_eq!(
1343 /// # ua.active_certifications_by_key(
1344 /// # P, t0, alice.primary_key().key()).count(),
1345 /// # 0);
1346 /// #
1347 /// # // Have Alice certify Bob's certificate.
1348 /// # let mut alice_signer = alice
1349 /// # .keys()
1350 /// # .with_policy(P, None)
1351 /// # .for_certification()
1352 /// # .next().expect("have a certification-capable key")
1353 /// # .key()
1354 /// # .clone()
1355 /// # .parts_into_secret().expect("have unencrypted key material")
1356 /// # .into_keypair().expect("have unencrypted key material");
1357 /// #
1358 /// # let mut bob = bob;
1359 /// # for i in 1..=2usize {
1360 /// # let ti = t0 + std::time::Duration::new(i as u64, 0);
1361 /// #
1362 /// # let certification = SignatureBuilder::new(SignatureType::GenericCertification)
1363 /// # .set_signature_creation_time(ti)?
1364 /// # .sign_userid_binding(
1365 /// # &mut alice_signer,
1366 /// # bob.primary_key().key(),
1367 /// # &UserID::from(bob_userid))?;
1368 /// # bob = bob.insert_packets(certification)?.0;
1369 /// #
1370 /// # let ua = bob.userids().next().expect("have a user id");
1371 /// # assert_eq!(
1372 /// # ua.valid_certifications_by_key(
1373 /// # P, ti, alice.primary_key().key()).count(),
1374 /// # i);
1375 /// #
1376 /// # assert_eq!(
1377 /// # ua.active_certifications_by_key(
1378 /// # P, ti, alice.primary_key().key()).count(),
1379 /// # 1);
1380 /// # }
1381 /// let ua = bob.userids().next().expect("have user id");
1382 ///
1383 /// let valid_certifications = ua.valid_certifications_by_key(
1384 /// P, None, alice.primary_key().key());
1385 /// // Alice certified Bob's certificate twice.
1386 /// assert_eq!(valid_certifications.count(), 2);
1387 ///
1388 /// let active_certifications = ua.active_certifications_by_key(
1389 /// P, None, alice.primary_key().key());
1390 /// // But only the most recent one is active.
1391 /// assert_eq!(active_certifications.count(), 1);
1392 /// # Ok(()) }
1393 /// ```
1394 pub fn valid_certifications_by_key<T, PK>(&self,
1395 policy: &'a dyn Policy,
1396 reference_time: T,
1397 issuer: PK)
1398 -> impl Iterator<Item=&Signature> + Send + Sync
1399 where
1400 T: Into<Option<time::SystemTime>>,
1401 PK: Into<&'a packet::Key<packet::key::PublicParts,
1402 packet::key::UnspecifiedRole>>,
1403 {
1404 let reference_time = reference_time.into();
1405 let issuer = issuer.into();
1406
1407 self.valid_certifications_by_key_(
1408 policy, reference_time, issuer, false,
1409 self.certifications(),
1410 |sig| {
1411 sig.clone().verify_userid_binding(
1412 issuer,
1413 self.cert.primary_key().key(),
1414 self.userid())
1415 })
1416 }
1417
1418 /// Returns any active third-party certifications issued by the
1419 /// specified key.
1420 ///
1421 /// This function is like
1422 /// [`UserIDAmalgamation::valid_certifications_by_key`], but it
1423 /// only returns active certifications. Active certifications are
1424 /// the most recent valid certifications with respect to the
1425 /// reference time.
1426 ///
1427 /// Although there is normally only a single active certification,
1428 /// there can be multiple certifications with the same timestamp.
1429 /// In this case, all of them are returned.
1430 ///
1431 /// Unlike self-signatures, multiple third-party certifications
1432 /// issued by the same key at the same time can be sensible. For
1433 /// instance, Alice may fully trust a CA for user IDs in a
1434 /// particular domain, and partially trust it for everything else.
1435 /// This can only be expressed using multiple certifications.
1436 ///
1437 /// This method is implemented on a [`UserIDAmalgamation`] and not
1438 /// a [`ValidUserIDAmalgamation`], because a third-party
1439 /// certification does not require the user ID to be self-signed.
1440 ///
1441 /// # Examples
1442 ///
1443 /// See the examples for
1444 /// [`UserIDAmalgamation::valid_certifications_by_key`].
1445 pub fn active_certifications_by_key<T, PK>(&self,
1446 policy: &'a dyn Policy,
1447 reference_time: T,
1448 issuer: PK)
1449 -> impl Iterator<Item=&Signature> + Send + Sync
1450 where
1451 T: Into<Option<time::SystemTime>>,
1452 PK: Into<&'a packet::Key<packet::key::PublicParts,
1453 packet::key::UnspecifiedRole>>,
1454 {
1455 let reference_time = reference_time.into();
1456 let issuer = issuer.into();
1457
1458 self.valid_certifications_by_key_(
1459 policy, reference_time, issuer, true,
1460 self.certifications(),
1461 |sig| {
1462 sig.clone().verify_userid_binding(
1463 issuer,
1464 self.cert.primary_key().key(),
1465 self.userid())
1466 })
1467 }
1468
1469 /// Returns the third-party revocations issued by the specified
1470 /// key, and valid at the specified time.
1471 ///
1472 /// This function returns the revocations issued by the specified
1473 /// key. Specifically, it returns a revocation if:
1474 ///
1475 /// - it is well-formed,
1476 /// - it is live with respect to the reference time,
1477 /// - it conforms to the policy, and
1478 /// - the signature is cryptographically valid.
1479 ///
1480 /// This method is implemented on a [`UserIDAmalgamation`] and not
1481 /// a [`ValidUserIDAmalgamation`], because a third-party
1482 /// revocation does not require the user ID to be self-signed.
1483 ///
1484 /// # Examples
1485 ///
1486 /// Alice revokes a user ID on Bob's certificate.
1487 ///
1488 /// ```rust
1489 /// use sequoia_openpgp as openpgp;
1490 /// use openpgp::cert::prelude::*;
1491 /// # use openpgp::Packet;
1492 /// # use openpgp::packet::signature::SignatureBuilder;
1493 /// # use openpgp::packet::UserID;
1494 /// use openpgp::policy::StandardPolicy;
1495 /// # use openpgp::types::ReasonForRevocation;
1496 /// # use openpgp::types::SignatureType;
1497 ///
1498 /// const P: &StandardPolicy = &StandardPolicy::new();
1499 ///
1500 /// # fn main() -> openpgp::Result<()> {
1501 /// # let epoch = std::time::SystemTime::now()
1502 /// # - std::time::Duration::new(100, 0);
1503 /// # let t0 = epoch;
1504 /// # let t1 = epoch + std::time::Duration::new(1, 0);
1505 /// #
1506 /// # let (alice, _) = CertBuilder::new()
1507 /// # .set_creation_time(t0)
1508 /// # .add_userid("<alice@example.org>")
1509 /// # .generate()
1510 /// # .unwrap();
1511 /// let alice: Cert = // ...
1512 /// # alice;
1513 /// #
1514 /// # let bob_userid = "<bob@example.org>";
1515 /// # let (bob, _) = CertBuilder::new()
1516 /// # .set_creation_time(t0)
1517 /// # .add_userid(bob_userid)
1518 /// # .generate()
1519 /// # .unwrap();
1520 /// let bob: Cert = // ...
1521 /// # bob;
1522 ///
1523 /// # // Alice has not certified Bob's User ID.
1524 /// # let ua = bob.userids().next().expect("have a user id");
1525 /// # assert_eq!(
1526 /// # ua.active_certifications_by_key(
1527 /// # P, t0, alice.primary_key().key()).count(),
1528 /// # 0);
1529 /// #
1530 /// # // Have Alice certify Bob's certificate.
1531 /// # let mut alice_signer = alice
1532 /// # .keys()
1533 /// # .with_policy(P, None)
1534 /// # .for_certification()
1535 /// # .next().expect("have a certification-capable key")
1536 /// # .key()
1537 /// # .clone()
1538 /// # .parts_into_secret().expect("have unencrypted key material")
1539 /// # .into_keypair().expect("have unencrypted key material");
1540 /// #
1541 /// # let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
1542 /// # .set_signature_creation_time(t1)?
1543 /// # .set_reason_for_revocation(
1544 /// # ReasonForRevocation::UIDRetired, b"")?
1545 /// # .sign_userid_binding(
1546 /// # &mut alice_signer,
1547 /// # bob.primary_key().key(),
1548 /// # &UserID::from(bob_userid))?;
1549 /// # let bob = bob.insert_packets([
1550 /// # Packet::from(UserID::from(bob_userid)),
1551 /// # Packet::from(certification),
1552 /// # ])?.0;
1553 /// let ua = bob.userids().next().expect("have user id");
1554 ///
1555 /// let revs = ua.valid_third_party_revocations_by_key(
1556 /// P, None, alice.primary_key().key());
1557 /// // Alice revoked the User ID.
1558 /// assert_eq!(revs.count(), 1);
1559 /// # Ok(()) }
1560 /// ```
1561 pub fn valid_third_party_revocations_by_key<T, PK>(&self,
1562 policy: &'a dyn Policy,
1563 reference_time: T,
1564 issuer: PK)
1565 -> impl Iterator<Item=&Signature> + Send + Sync
1566 where
1567 T: Into<Option<time::SystemTime>>,
1568 PK: Into<&'a packet::Key<packet::key::PublicParts,
1569 packet::key::UnspecifiedRole>>,
1570 {
1571 let reference_time = reference_time.into();
1572 let issuer = issuer.into();
1573
1574 self.valid_certifications_by_key_(
1575 policy, reference_time, issuer, false,
1576 self.other_revocations(),
1577 |sig| {
1578 sig.clone().verify_userid_revocation(
1579 issuer,
1580 self.cert.primary_key().key(),
1581 self.userid())
1582 })
1583 }
1584
1585 /// Approves of third-party certifications.
1586 ///
1587 /// This feature is [experimental](crate#experimental-features).
1588 ///
1589 /// Allows the certificate owner to approve of third party
1590 /// certifications. See [Approved Certifications subpacket] for
1591 /// details. This can be used to address certificate flooding
1592 /// concerns.
1593 ///
1594 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
1595 ///
1596 /// A policy is needed, because the expiration is updated by
1597 /// updating the current binding signatures.
1598 ///
1599 /// # Examples
1600 ///
1601 /// ```
1602 /// # use sequoia_openpgp as openpgp;
1603 /// # fn main() -> openpgp::Result<()> {
1604 /// # use openpgp::cert::prelude::*;
1605 /// # use openpgp::packet::signature::SignatureBuilder;
1606 /// # use openpgp::types::*;
1607 /// # let policy = &openpgp::policy::StandardPolicy::new();
1608 /// let (alice, _) = CertBuilder::new()
1609 /// .add_userid("alice@example.org")
1610 /// .generate()?;
1611 /// let mut alice_signer =
1612 /// alice.primary_key().key().clone().parts_into_secret()?
1613 /// .into_keypair()?;
1614 ///
1615 /// let (bob, _) = CertBuilder::new()
1616 /// .add_userid("bob@example.org")
1617 /// .generate()?;
1618 /// let mut bob_signer =
1619 /// bob.primary_key().key().clone().parts_into_secret()?
1620 /// .into_keypair()?;
1621 /// let bob_pristine = bob.clone();
1622 ///
1623 /// // Have Alice certify the binding between "bob@example.org" and
1624 /// // Bob's key.
1625 /// let alice_certifies_bob
1626 /// = bob.userids().next().unwrap().userid().bind(
1627 /// &mut alice_signer, &bob,
1628 /// SignatureBuilder::new(SignatureType::GenericCertification))?;
1629 /// let bob = bob.insert_packets(vec![alice_certifies_bob.clone()])?.0;
1630 ///
1631 /// // Have Bob approve of that certification.
1632 /// let bobs_uid = bob.userids().next().unwrap();
1633 /// let approvals =
1634 /// bobs_uid.approve_of_certifications(
1635 /// policy,
1636 /// None,
1637 /// &mut bob_signer,
1638 /// bobs_uid.certifications())?;
1639 /// let bob = bob.insert_packets(approvals)?.0;
1640 ///
1641 /// assert_eq!(bob.bad_signatures().count(), 0);
1642 /// assert_eq!(bob.userids().next().unwrap().certifications().next(),
1643 /// Some(&alice_certifies_bob));
1644 /// # Ok(()) }
1645 /// ```
1646 pub fn approve_of_certifications<T, C, S>(&self,
1647 policy: &dyn Policy,
1648 time: T,
1649 primary_signer: &mut dyn Signer,
1650 certifications: C)
1651 -> Result<Vec<Signature>>
1652 where T: Into<Option<time::SystemTime>>,
1653 C: IntoIterator<Item = S>,
1654 S: Borrow<Signature>,
1655 {
1656 let time = time.into();
1657 let certifications = certifications.into_iter()
1658 .collect::<Vec<_>>();
1659
1660 // Check if there is a previous attestation. If so, we need
1661 // that to robustly override it.
1662 let old = self.clone()
1663 .with_policy(policy, time)
1664 .ok()
1665 .and_then(
1666 |v| v.certification_approval_key_signatures().next().cloned());
1667
1668 approve_of_certifications_common(self.cert().primary_key().key(),
1669 self.userid(),
1670 old, time, primary_signer,
1671 &certifications)
1672 }
1673}
1674
1675impl<'a> UserAttributeAmalgamation<'a> {
1676 /// Returns a reference to the User Attribute.
1677 ///
1678 /// This is just a type-specific alias for
1679 /// [`ComponentAmalgamation::component`].
1680 ///
1681 /// # Examples
1682 ///
1683 /// ```
1684 /// # use sequoia_openpgp as openpgp;
1685 /// # use openpgp::cert::prelude::*;
1686 /// #
1687 /// # fn main() -> openpgp::Result<()> {
1688 /// # let (cert, _) =
1689 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1690 /// # .generate()?;
1691 /// // Display some information about the User Attributes
1692 /// for ua in cert.user_attributes() {
1693 /// eprintln!(" - {:?}", ua.user_attribute());
1694 /// }
1695 /// # Ok(()) }
1696 /// ```
1697 pub fn user_attribute(&self) -> &'a UserAttribute {
1698 self.component()
1699 }
1700
1701 /// Returns the User Attribute's revocation status at time `t`.
1702 ///
1703 /// A User Attribute is revoked at time `t` if:
1704 ///
1705 /// - There is a live revocation at time `t` that is newer than
1706 /// all live self signatures at time `t`.
1707 ///
1708 /// Note: Certs and subkeys have different criteria from User IDs
1709 /// and User Attributes.
1710 ///
1711 /// Note: this only returns whether this User Attribute is revoked;
1712 /// it does not imply anything about the Cert or other components.
1713 //
1714 /// # Examples
1715 ///
1716 /// ```
1717 /// # use sequoia_openpgp as openpgp;
1718 /// # use openpgp::cert::prelude::*;
1719 /// use openpgp::policy::StandardPolicy;
1720 /// #
1721 /// # fn main() -> openpgp::Result<()> {
1722 /// let p = &StandardPolicy::new();
1723 ///
1724 /// # let (cert, _) =
1725 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1726 /// # .generate()?;
1727 /// // Display the User Attributes' revocation status.
1728 /// for (i, ua) in cert.user_attributes().enumerate() {
1729 /// eprintln!(" Revocation status of User Attribute #{}: {:?}",
1730 /// i, ua.revocation_status(p, None));
1731 /// }
1732 /// # Ok(()) }
1733 /// ```
1734 pub fn revocation_status<T>(&self, policy: &dyn Policy, t: T)
1735 -> RevocationStatus
1736 where
1737 T: Into<Option<time::SystemTime>>,
1738 {
1739 let t = t.into();
1740 self.bundle().revocation_status(policy, t)
1741 }
1742
1743 /// Approves of third-party certifications.
1744 ///
1745 /// This feature is [experimental](crate#experimental-features).
1746 ///
1747 /// Allows the certificate owner to approve of third party
1748 /// certifications. See [Approved Certifications subpacket] for
1749 /// details. This can be used to address certificate flooding
1750 /// concerns.
1751 ///
1752 /// A policy is needed, because the expiration is updated by
1753 /// updating the current binding signatures.
1754 ///
1755 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
1756 ///
1757 /// # Examples
1758 ///
1759 /// See [`UserIDAmalgamation::approve_of_certifications#examples`].
1760 ///
1761 /// [`UserIDAmalgamation::approve_of_certifications#examples`]: UserIDAmalgamation#examples
1762 // The explicit link works around a bug in rustdoc.
1763 pub fn approve_of_certifications<T, C, S>(&self,
1764 policy: &dyn Policy,
1765 time: T,
1766 primary_signer: &mut dyn Signer,
1767 certifications: C)
1768 -> Result<Vec<Signature>>
1769 where T: Into<Option<time::SystemTime>>,
1770 C: IntoIterator<Item = S>,
1771 S: Borrow<Signature>,
1772 {
1773 let time = time.into();
1774 let certifications = certifications.into_iter()
1775 .collect::<Vec<_>>();
1776
1777 // Check if there is a previous attestation. If so, we need
1778 // that to robustly override it.
1779 let old = self.clone()
1780 .with_policy(policy, time)
1781 .ok()
1782 .and_then(
1783 |v| v.certification_approval_key_signatures().next().cloned());
1784
1785 approve_of_certifications_common(self.cert().primary_key().key(),
1786 self.user_attribute(),
1787 old, time, primary_signer,
1788 &certifications)
1789 }
1790}
1791
1792/// Approves of third-party certifications.
1793fn approve_of_certifications_common<S>(key: &Key<PublicParts, PrimaryRole>,
1794 component: &dyn Hash,
1795 old_attestation: Option<Signature>,
1796 time: Option<SystemTime>,
1797 primary_signer: &mut dyn Signer,
1798 certifications: &[S])
1799 -> Result<Vec<Signature>>
1800where
1801 S: Borrow<Signature>,
1802{
1803 use crate::{
1804 packet::signature::{SignatureBuilder, subpacket::SubpacketArea},
1805 serialize::MarshalInto,
1806 };
1807
1808 // Fix the time.
1809 let now = time.unwrap_or_else(crate::now);
1810
1811 // Fix the algorithm.
1812 let hash_algo = HashAlgorithm::default();
1813 let digest_size = hash_algo.digest_size()?;
1814
1815 let mut attestations = Vec::new();
1816 for certification in certifications {
1817 let mut h = hash_algo.context()?
1818 .for_signature(primary_signer.public().version());
1819 certification.borrow().hash_for_confirmation(&mut h)?;
1820 attestations.push(h.into_digest()?);
1821 }
1822
1823 // Hashes SHOULD be sorted.
1824 attestations.sort();
1825
1826 // All attestation signatures we generate for this component
1827 // should have the same creation time. Fix it now. We also like
1828 // our signatures to be newer than any existing signatures. Do so
1829 // by using the old attestation as template.
1830 let template = if let Some(old) = old_attestation {
1831 let mut s = SignatureBuilder::from(old)
1832 .set_reference_time(now)?;
1833 s.hashed_area_mut().clear();
1834 s.unhashed_area_mut().clear();
1835 s
1836 } else {
1837 // Backdate the signature a little so that we can immediately
1838 // override it.
1839 use crate::packet::signature::SIG_BACKDATE_BY;
1840
1841 let mut creation_time =
1842 now - time::Duration::new(SIG_BACKDATE_BY, 0);
1843
1844 // ... but don't backdate it further than the key's creation
1845 // time, which would make it invalid.
1846 let key_creation_time = primary_signer.public().creation_time();
1847 if creation_time < key_creation_time {
1848 // ... unless that would make it is later than now.
1849 creation_time = key_creation_time.min(now);
1850 }
1851
1852 let template = SignatureBuilder::new(SignatureType::CertificationApproval)
1853 .set_signature_creation_time(creation_time)?;
1854 template
1855
1856 };
1857
1858 let template = template
1859 .set_hash_algo(hash_algo);
1860
1861 // Compute the available space in the hashed area. For this,
1862 // it is important that template.pre_sign has been called.
1863 let available_space = {
1864 // But, we do it on a clone, so that `template` is still not
1865 // initialized.
1866 let t = template.clone().pre_sign(primary_signer)?;
1867
1868 SubpacketArea::MAX_SIZE - t.hashed_area().serialized_len()
1869 };
1870
1871 // Reserve space for the subpacket header, length and tag.
1872 const SUBPACKET_HEADER_MAX_LEN: usize = 5 + 1;
1873
1874 // Compute the chunk size for each signature.
1875 let digests_per_sig =
1876 (available_space - SUBPACKET_HEADER_MAX_LEN) / digest_size;
1877
1878 // Now create the signatures.
1879 let mut sigs = Vec::new();
1880 for digests in attestations.chunks(digests_per_sig) {
1881 // Hash the components. First, initialize the salt.
1882 let t = template.clone().pre_sign(primary_signer)?;
1883
1884 let mut hash = hash_algo.context()?
1885 .for_signature(primary_signer.public().version());
1886
1887 if let Some(salt) = t.sb_version.salt() {
1888 hash.update(salt);
1889 }
1890 key.hash(&mut hash)?;
1891 component.hash(&mut hash)?;
1892
1893 sigs.push(t
1894 .set_approved_certifications(digests)?
1895 .sign_hash(primary_signer, hash)?);
1896 }
1897
1898 if attestations.is_empty() {
1899 // The certificate owner can withdraw attestations by issuing
1900 // an empty attestation key signature.
1901 assert!(sigs.is_empty());
1902
1903 // Hash the components. First, initialize the salt.
1904 let t = template.clone().pre_sign(primary_signer)?;
1905
1906 let mut hash = hash_algo.context()?
1907 .for_signature(primary_signer.public().version());
1908
1909 if let Some(salt) = t.sb_version.salt() {
1910 hash.update(salt);
1911 }
1912 key.hash(&mut hash)?;
1913 component.hash(&mut hash)?;
1914
1915 sigs.push(t
1916 .set_approved_certifications(Option::<&[u8]>::None)?
1917 .sign_hash(primary_signer, hash.clone())?);
1918 }
1919
1920 Ok(sigs)
1921}
1922
1923impl<'a> UnknownComponentAmalgamation<'a> {
1924 /// Returns a reference to the Unknown packet.
1925 ///
1926 /// This is just a type-specific alias for
1927 /// [`ComponentAmalgamation::component`].
1928 ///
1929 /// # Examples
1930 ///
1931 /// ```
1932 /// # use sequoia_openpgp as openpgp;
1933 /// # use openpgp::cert::prelude::*;
1934 /// #
1935 /// # fn main() -> openpgp::Result<()> {
1936 /// # let (cert, _) =
1937 /// # CertBuilder::general_purpose(Some("alice@example.org"))
1938 /// # .generate()?;
1939 /// // Display some information about the Unknown components.
1940 /// for u in cert.unknowns() {
1941 /// eprintln!(" - {:?}", u.unknown());
1942 /// }
1943 /// # Ok(()) }
1944 /// ```
1945 pub fn unknown(&self) -> &'a Unknown {
1946 self.component()
1947 }
1948}
1949
1950/// A `ComponentAmalgamation` plus a `Policy` and a reference time.
1951///
1952/// A `ValidComponentAmalgamation` combines a
1953/// [`ComponentAmalgamation`] with a [`Policy`] and a reference time.
1954/// This allows it to implement the [`ValidAmalgamation`] trait, which
1955/// provides methods that require a [`Policy`] and a reference time.
1956/// Although `ComponentAmalgamation` could implement these methods by
1957/// requiring that the caller explicitly pass them in, embedding them
1958/// in the `ValidComponentAmalgamation` helps ensure that multipart
1959/// operations, even those that span multiple functions, use the same
1960/// `Policy` and reference time.
1961///
1962/// A `ValidComponentAmalgamation` is typically obtained by
1963/// transforming a `ComponentAmalgamation` using
1964/// [`ValidateAmalgamation::with_policy`]. A
1965/// [`ComponentAmalgamationIter`] can also be changed to yield
1966/// `ValidComponentAmalgamation`s.
1967///
1968/// A `ValidComponentAmalgamation` is guaranteed to come from a valid
1969/// certificate, and have a valid and live binding signature at the
1970/// specified reference time. Note: this only means that the binding
1971/// signatures are live; it says nothing about whether the
1972/// *certificate* is live. If you care about that, then you need to
1973/// check it separately.
1974///
1975/// # Examples
1976///
1977/// Print out information about all non-revoked User IDs.
1978///
1979/// ```
1980/// # use sequoia_openpgp as openpgp;
1981/// use openpgp::cert::prelude::*;
1982/// use openpgp::packet::prelude::*;
1983/// use openpgp::policy::StandardPolicy;
1984/// use openpgp::types::RevocationStatus;
1985///
1986/// # fn main() -> openpgp::Result<()> {
1987/// let p = &StandardPolicy::new();
1988/// # let (cert, _) = CertBuilder::new()
1989/// # .add_userid("Alice")
1990/// # .add_signing_subkey()
1991/// # .add_transport_encryption_subkey()
1992/// # .generate()?;
1993/// for u in cert.userids() {
1994/// // Create a `ValidComponentAmalgamation`. This may fail if
1995/// // there are no binding signatures that are accepted by the
1996/// // policy and that are live right now.
1997/// let u = u.with_policy(p, None)?;
1998///
1999/// // Before using the User ID, we still need to check that it is
2000/// // not revoked; `ComponentAmalgamation::with_policy` ensures
2001/// // that there is a valid *binding signature*, not that the
2002/// // `ComponentAmalgamation` is valid.
2003/// //
2004/// // Note: `ValidComponentAmalgamation::revocation_status` and
2005/// // `Preferences::preferred_symmetric_algorithms` use the
2006/// // embedded policy and timestamp. Even though we used `None` for
2007/// // the timestamp (i.e., now), they are guaranteed to use the same
2008/// // timestamp, because `with_policy` eagerly transforms it into
2009/// // the current time.
2010/// //
2011/// // Note: we only check whether the User ID is not revoked. If
2012/// // we were using a key, we'd also want to check that it is alive.
2013/// // (Keys can expire, but User IDs cannot.)
2014/// if let RevocationStatus::Revoked(_revs) = u.revocation_status() {
2015/// // Revoked by the key owner. (If we care about
2016/// // designated revokers, then we need to check those
2017/// // ourselves.)
2018/// } else {
2019/// // Print information about the User ID.
2020/// eprintln!("{}: preferred symmetric algorithms: {:?}",
2021/// String::from_utf8_lossy(u.userid().value()),
2022/// u.preferred_symmetric_algorithms());
2023/// }
2024/// }
2025/// # Ok(()) }
2026/// ```
2027///
2028/// [`Policy`]: crate::policy::Policy
2029#[derive(Debug)]
2030pub struct ValidComponentAmalgamation<'a, C> {
2031 ca: ComponentAmalgamation<'a, C>,
2032 cert: ValidCert<'a>,
2033 // The binding signature at time `time`. (This is just a cache.)
2034 binding_signature: &'a Signature,
2035}
2036assert_send_and_sync!(ValidComponentAmalgamation<'_, C> where C);
2037
2038impl<'a, C> ValidComponentAmalgamation<'a, C> {
2039 /// Returns the valid amalgamation's associated certificate.
2040 ///
2041 /// ```
2042 /// # use sequoia_openpgp as openpgp;
2043 /// # use openpgp::cert::prelude::*;
2044 /// #
2045 /// # fn main() -> openpgp::Result<()> {
2046 /// # let (cert, _) =
2047 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2048 /// # .generate()?;
2049 /// for u in cert.userids() {
2050 /// // It's not only an identical `Cert`, it's the same one.
2051 /// assert!(std::ptr::eq(u.cert(), &cert));
2052 /// }
2053 /// # Ok(()) }
2054 /// ```
2055 pub fn cert(&self) -> &'a Cert {
2056 self.ca.cert()
2057 }
2058
2059 /// Returns the valid amalgamation's active binding signature.
2060 ///
2061 /// The active binding signature is the most recent, non-revoked
2062 /// self-signature that is valid according to the `policy` and
2063 /// alive at time `t` (`creation time <= t`, `t < expiry`). If
2064 /// there are multiple such signatures then the signatures are
2065 /// ordered by their MPIs interpreted as byte strings.
2066 ///
2067 /// # Examples
2068 ///
2069 /// ```
2070 /// # use sequoia_openpgp as openpgp;
2071 /// # use openpgp::cert::prelude::*;
2072 /// use openpgp::policy::StandardPolicy;
2073 /// #
2074 /// # fn main() -> openpgp::Result<()> {
2075 /// let p = &StandardPolicy::new();
2076 ///
2077 /// # let (cert, _) =
2078 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2079 /// # .generate()?;
2080 /// // Display information about each User ID's current active
2081 /// // binding signature (the `time` parameter is `None`), if any.
2082 /// for ua in cert.with_policy(p, None)?.userids() {
2083 /// eprintln!("{:?}", ua.binding_signature());
2084 /// }
2085 /// # Ok(()) }
2086 /// ```
2087 pub fn binding_signature(&self) -> &'a Signature {
2088 self.binding_signature
2089 }
2090
2091 /// Returns the valid amalgamation's amalgamation.
2092 ///
2093 /// # Examples
2094 ///
2095 /// ```
2096 /// # use sequoia_openpgp as openpgp;
2097 /// # use openpgp::cert::prelude::*;
2098 /// use openpgp::policy::StandardPolicy;
2099 ///
2100 /// # fn main() -> openpgp::Result<()> {
2101 /// let p = &StandardPolicy::new();
2102 ///
2103 /// # let (cert, _) = CertBuilder::new()
2104 /// # .add_userid("Alice")
2105 /// # .add_signing_subkey()
2106 /// # .add_transport_encryption_subkey()
2107 /// # .generate()?;
2108 /// // Get a user ID amalgamation.
2109 /// let ua = cert.userids().next().expect("added one");
2110 ///
2111 /// // Validate it, yielding a valid component amalgamation.
2112 /// let vua = ua.with_policy(p, None)?;
2113 ///
2114 /// // And here we get the amalgamation back.
2115 /// let ua2 = vua.amalgamation();
2116 /// assert_eq!(&ua, ua2);
2117 /// # Ok(()) }
2118 /// ```
2119 pub fn amalgamation(&self) -> &ComponentAmalgamation<'a, C> {
2120 &self.ca
2121 }
2122
2123 /// Returns this valid amalgamation's bundle.
2124 ///
2125 /// # Examples
2126 ///
2127 /// ```
2128 /// # fn main() -> sequoia_openpgp::Result<()> {
2129 /// # use sequoia_openpgp as openpgp;
2130 /// use openpgp::cert::prelude::*;
2131 /// use openpgp::packet::prelude::*;
2132 /// let p = &openpgp::policy::StandardPolicy::new();
2133 ///
2134 /// # let (cert, _) = CertBuilder::new()
2135 /// # .add_userid("Alice")
2136 /// # .add_signing_subkey()
2137 /// # .add_transport_encryption_subkey()
2138 /// # .generate()?;
2139 /// cert.with_policy(p, None)?.userids()
2140 /// .map(|ua| ua.bundle())
2141 /// .collect::<Vec<&ComponentBundle<_>>>();
2142 /// # Ok(()) }
2143 /// ```
2144 pub fn bundle(&self) -> &'a ComponentBundle<C> {
2145 self.ca.bundle()
2146 }
2147
2148 /// Returns this valid amalgamation's component.
2149 ///
2150 /// # Examples
2151 ///
2152 /// ```
2153 /// # use sequoia_openpgp as openpgp;
2154 /// # use openpgp::cert::prelude::*;
2155 /// # fn main() -> openpgp::Result<()> {
2156 /// # let (cert, _) =
2157 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2158 /// # .generate()?;
2159 /// let p = &openpgp::policy::StandardPolicy::new();
2160 ///
2161 /// // Display some information about any userid components.
2162 /// for u in cert.with_policy(p, None)?.userids() {
2163 /// eprintln!(" - {:?}", u.component());
2164 /// }
2165 /// # Ok(()) }
2166 /// ```
2167 pub fn component(&self) -> &'a C {
2168 self.bundle().component()
2169 }
2170}
2171
2172impl<'a, C> ValidComponentAmalgamation<'a, C>
2173where
2174 C: Send + Sync,
2175{
2176 /// Returns the valid amalgamation's self-signatures.
2177 ///
2178 /// The signatures are validated, and they are sorted by their
2179 /// creation time, most recent first. This method only returns
2180 /// signatures that are valid under the current policy.
2181 ///
2182 /// # Examples
2183 ///
2184 /// ```
2185 /// # use sequoia_openpgp as openpgp;
2186 /// # use openpgp::cert::prelude::*;
2187 /// use openpgp::policy::StandardPolicy;
2188 /// #
2189 /// # fn main() -> openpgp::Result<()> {
2190 /// let p = &StandardPolicy::new();
2191 ///
2192 /// # let (cert, _) =
2193 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2194 /// # .generate()?;
2195 /// for (i, ka) in cert.with_policy(p, None)?.keys().enumerate() {
2196 /// eprintln!("Key #{} ({}) has {:?} self signatures",
2197 /// i, ka.key().fingerprint(),
2198 /// ka.self_signatures().count());
2199 /// }
2200 /// # Ok(()) }
2201 /// ```
2202 pub fn self_signatures(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
2203 let policy = self.cert.policy();
2204 let has = self.ca.bundle().hash_algo_security;
2205
2206 self.ca.self_signatures()
2207 .filter(move |sig| policy.signature(sig, has).is_ok())
2208 }
2209
2210 /// Returns the component's third-party certifications.
2211 ///
2212 /// The signatures are *not* validated. They are sorted by their
2213 /// creation time, most recent first. This method only returns
2214 /// signatures that are valid under the current policy.
2215 ///
2216 /// # Examples
2217 ///
2218 /// ```
2219 /// # use sequoia_openpgp as openpgp;
2220 /// # use openpgp::cert::prelude::*;
2221 /// use openpgp::policy::StandardPolicy;
2222 /// #
2223 /// # fn main() -> openpgp::Result<()> {
2224 /// let p = &StandardPolicy::new();
2225 ///
2226 /// # let (cert, _) =
2227 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2228 /// # .generate()?;
2229 /// for ua in cert.with_policy(p, None)?.userids() {
2230 /// eprintln!("User ID {} has {:?} unverified, third-party certifications",
2231 /// String::from_utf8_lossy(ua.userid().value()),
2232 /// ua.certifications().count());
2233 /// }
2234 /// # Ok(()) }
2235 /// ```
2236 pub fn certifications(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
2237 let policy = self.cert.policy();
2238
2239 self.ca.certifications()
2240 .filter(move |sig| policy.signature(sig,
2241 HashAlgoSecurity::CollisionResistance).is_ok())
2242 }
2243
2244 /// Returns the valid amalgamation's revocations that were issued
2245 /// by the certificate holder.
2246 ///
2247 /// The revocations are validated, and they are sorted by their
2248 /// creation time, most recent first. This method only returns
2249 /// signatures that are valid under the current policy.
2250 ///
2251 /// # Examples
2252 ///
2253 /// ```
2254 /// # use sequoia_openpgp as openpgp;
2255 /// # use openpgp::cert::prelude::*;
2256 /// use openpgp::policy::StandardPolicy;
2257 /// #
2258 /// # fn main() -> openpgp::Result<()> {
2259 /// let p = &StandardPolicy::new();
2260 ///
2261 /// # let (cert, _) =
2262 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2263 /// # .generate()?;
2264 /// for u in cert.with_policy(p, None)?.userids() {
2265 /// eprintln!("User ID {} has {:?} revocation certificates.",
2266 /// String::from_utf8_lossy(u.userid().value()),
2267 /// u.self_revocations().count());
2268 /// }
2269 /// # Ok(()) }
2270 /// ```
2271 pub fn self_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
2272 let policy = self.cert.policy();
2273 let has = self.ca.bundle().hash_algo_security;
2274
2275 self.ca.self_revocations()
2276 .filter(move |sig| policy.signature(sig, has).is_ok())
2277 }
2278
2279 /// Returns the valid amalgamation's revocations that were issued
2280 /// by other certificates.
2281 ///
2282 /// The revocations are *not* validated. They are sorted by their
2283 /// creation time, most recent first. This method only returns
2284 /// signatures that are valid under the current policy.
2285 ///
2286 /// # Examples
2287 ///
2288 /// ```
2289 /// # use sequoia_openpgp as openpgp;
2290 /// # use openpgp::cert::prelude::*;
2291 /// use openpgp::policy::StandardPolicy;
2292 /// #
2293 /// # fn main() -> openpgp::Result<()> {
2294 /// let p = &StandardPolicy::new();
2295 ///
2296 /// # let (cert, _) =
2297 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2298 /// # .generate()?;
2299 /// for u in cert.with_policy(p, None)?.userids() {
2300 /// eprintln!("User ID {} has {:?} unverified, third-party revocation certificates.",
2301 /// String::from_utf8_lossy(u.userid().value()),
2302 /// u.other_revocations().count());
2303 /// }
2304 /// # Ok(()) }
2305 /// ```
2306 pub fn other_revocations(&self) -> impl Iterator<Item=&'a Signature> + Send + Sync + 'a {
2307 let policy = self.cert.policy();
2308
2309 self.ca.other_revocations()
2310 .filter(move |sig| policy.signature(sig,
2311 HashAlgoSecurity::CollisionResistance).is_ok())
2312 }
2313
2314 /// Returns all of the valid amalgamation's Certification Approval
2315 /// Key Signatures.
2316 ///
2317 /// This feature is [experimental](crate#experimental-features).
2318 ///
2319 /// The signatures are validated, and they are sorted by their
2320 /// creation time, most recent first.
2321 ///
2322 /// A certificate owner can use Certification Approval Key
2323 /// Signatures to approve of third party certifications.
2324 /// Currently, only userid and user attribute certifications can
2325 /// be approved. See [Approved Certifications subpacket] for
2326 /// details.
2327 ///
2328 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
2329 ///
2330 /// # Examples
2331 ///
2332 /// ```
2333 /// # use sequoia_openpgp as openpgp;
2334 /// # fn main() -> openpgp::Result<()> {
2335 /// # use openpgp::cert::prelude::*;
2336 /// use openpgp::policy::StandardPolicy;
2337 /// let p = &StandardPolicy::new();
2338 ///
2339 /// # let (cert, _) =
2340 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2341 /// # .generate()?;
2342 /// for (i, uid) in cert.with_policy(p, None)?.userids().enumerate() {
2343 /// eprintln!("UserID #{} ({:?}) has {:?} certification approval key signatures",
2344 /// i, uid.userid().email(),
2345 /// uid.approvals().count());
2346 /// }
2347 /// # Ok(()) }
2348 /// ```
2349 pub fn approvals(&self)
2350 -> impl Iterator<Item = &'a Signature> + Send + Sync + 'a
2351 {
2352 let policy = self.cert.policy();
2353 let has = self.ca.bundle().hash_algo_security;
2354
2355 self.ca.approvals()
2356 .filter(move |sig| policy.signature(sig, has).is_ok())
2357 }
2358
2359 /// Returns all of the valid amalgamations's signatures.
2360 ///
2361 /// Only the self-signatures are validated. The signatures are
2362 /// sorted first by type, then by creation time. The self
2363 /// revocations come first, then the self signatures,
2364 /// then any certification approval key signatures,
2365 /// certifications, and third-party revocations coming last. This
2366 /// function may return additional types of signatures that could
2367 /// be associated to this component.
2368 ///
2369 /// This method only returns signatures that are valid under the
2370 /// current policy.
2371 ///
2372 /// # Examples
2373 ///
2374 /// ```
2375 /// # use sequoia_openpgp as openpgp;
2376 /// # use openpgp::cert::prelude::*;
2377 /// use openpgp::policy::StandardPolicy;
2378 /// #
2379 /// # fn main() -> openpgp::Result<()> {
2380 /// let p = &StandardPolicy::new();
2381 ///
2382 /// # let (cert, _) =
2383 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2384 /// # .generate()?;
2385 /// for (i, ka) in cert.with_policy(p, None)?.keys().enumerate() {
2386 /// eprintln!("Key #{} ({}) has {:?} signatures",
2387 /// i, ka.key().fingerprint(),
2388 /// ka.signatures().count());
2389 /// }
2390 /// # Ok(()) }
2391 /// ```
2392 pub fn signatures(&self)
2393 -> impl Iterator<Item = &'a Signature> + Send + Sync + 'a {
2394 let policy = self.cert.policy();
2395
2396 self.ca.signatures()
2397 .filter(move |sig| policy.signature(sig,
2398 HashAlgoSecurity::CollisionResistance).is_ok())
2399 }
2400}
2401
2402/// A Valid User ID and its associated data.
2403///
2404/// A specialized version of [`ValidComponentAmalgamation`].
2405///
2406pub type ValidUserIDAmalgamation<'a> = ValidComponentAmalgamation<'a, UserID>;
2407
2408impl<'a> ValidUserIDAmalgamation<'a> {
2409 /// Returns a reference to the User ID.
2410 ///
2411 /// This is just a type-specific alias for
2412 /// [`ValidComponentAmalgamation::component`].
2413 ///
2414 /// # Examples
2415 ///
2416 /// ```
2417 /// # use sequoia_openpgp as openpgp;
2418 /// # use openpgp::cert::prelude::*;
2419 /// #
2420 /// # fn main() -> openpgp::Result<()> {
2421 /// # let (cert, _) =
2422 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2423 /// # .generate()?;
2424 /// // Display some information about the User IDs.
2425 /// for ua in cert.userids() {
2426 /// eprintln!(" - {:?}", ua.userid());
2427 /// }
2428 /// # Ok(()) }
2429 /// ```
2430 pub fn userid(&self) -> &'a UserID {
2431 self.component()
2432 }
2433
2434 /// Returns the user ID's approved third-party certifications.
2435 ///
2436 /// This feature is [experimental](crate#experimental-features).
2437 ///
2438 /// Allows the certificate owner to approve of third party
2439 /// certifications. See [Approved Certification subpacket] for
2440 /// details. This can be used to address certificate flooding
2441 /// concerns.
2442 ///
2443 /// This method only returns signatures that are valid under the
2444 /// current policy and are approved by the certificate holder.
2445 ///
2446 /// [Approved Certification subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
2447 pub fn approved_certifications(&self)
2448 -> impl Iterator<Item=&Signature> + Send + Sync
2449 {
2450 let mut hash_algo = None;
2451 let digests: std::collections::HashSet<_> =
2452 self.certification_approval_key_signatures()
2453 .filter_map(|sig| {
2454 sig.approved_certifications().ok()
2455 .map(|digest_iter| (sig, digest_iter))
2456 })
2457 .flat_map(|(sig, digest_iter)| {
2458 hash_algo = Some(sig.hash_algo());
2459 digest_iter
2460 })
2461 .collect();
2462
2463 self.certifications()
2464 .filter_map(move |sig| {
2465 let mut hash = hash_algo.and_then(|a| a.context().ok())?
2466 .for_signature(sig.version());
2467 sig.hash_for_confirmation(&mut hash).ok()?;
2468 let digest = hash.into_digest().ok()?;
2469 if digests.contains(&digest[..]) {
2470 Some(sig)
2471 } else {
2472 None
2473 }
2474 })
2475 }
2476
2477 /// Returns set of active certification approval key signatures.
2478 ///
2479 /// This feature is [experimental](crate#experimental-features).
2480 ///
2481 /// Returns the set of signatures with the newest valid signature
2482 /// creation time. Older signatures are not returned. The sum of
2483 /// all digests in these signatures are the set of approved
2484 /// third-party certifications.
2485 ///
2486 /// This interface is useful for pruning old certification
2487 /// approval key signatures when filtering a certificate.
2488 ///
2489 /// Note: This is a low-level interface. Consider using
2490 /// [`ValidUserIDAmalgamation::approved_certifications`] to
2491 /// iterate over all approved certifications.
2492 ///
2493 /// [`ValidUserIDAmalgamation::approved_certifications`]: ValidUserIDAmalgamation#method.approved_certifications
2494 // The explicit link works around a bug in rustdoc.
2495 pub fn certification_approval_key_signatures(&'a self)
2496 -> impl Iterator<Item=&'a Signature> + Send + Sync
2497 {
2498 let mut first = None;
2499
2500 // The newest valid signature will be returned first.
2501 self.ca.approvals()
2502 // First, filter out any invalid (e.g. too new) signatures.
2503 .filter(move |sig| self.cert.policy().signature(
2504 sig,
2505 HashAlgoSecurity::CollisionResistance).is_ok())
2506 .take_while(move |sig| {
2507 let time_hash = (
2508 if let Some(t) = sig.signature_creation_time() {
2509 (t, sig.hash_algo())
2510 } else {
2511 // Something is off. Just stop.
2512 return false;
2513 },
2514 sig.hash_algo());
2515
2516 if let Some(reference) = first {
2517 // Stop looking once we see an older signature or one
2518 // with a different hash algo.
2519 reference == time_hash
2520 } else {
2521 first = Some(time_hash);
2522 true
2523 }
2524 })
2525 }
2526
2527 /// Approves of third-party certifications.
2528 ///
2529 /// This feature is [experimental](crate#experimental-features).
2530 ///
2531 /// Allows the certificate owner to approve of third party
2532 /// certifications. See [Approved Certifications subpacket] for
2533 /// details. This can be used to address certificate flooding
2534 /// concerns.
2535 ///
2536 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
2537 ///
2538 /// # Examples
2539 ///
2540 /// ```
2541 /// # use sequoia_openpgp as openpgp;
2542 /// # fn main() -> openpgp::Result<()> {
2543 /// # use openpgp::cert::prelude::*;
2544 /// # use openpgp::packet::signature::SignatureBuilder;
2545 /// # use openpgp::types::*;
2546 /// # let policy = &openpgp::policy::StandardPolicy::new();
2547 /// let (alice, _) = CertBuilder::new()
2548 /// .add_userid("alice@example.org")
2549 /// .generate()?;
2550 /// let mut alice_signer =
2551 /// alice.primary_key().key().clone().parts_into_secret()?
2552 /// .into_keypair()?;
2553 ///
2554 /// let (bob, _) = CertBuilder::new()
2555 /// .add_userid("bob@example.org")
2556 /// .generate()?;
2557 /// let mut bob_signer =
2558 /// bob.primary_key().key().clone().parts_into_secret()?
2559 /// .into_keypair()?;
2560 /// let bob_pristine = bob.clone();
2561 ///
2562 /// // Have Alice certify the binding between "bob@example.org" and
2563 /// // Bob's key.
2564 /// let alice_certifies_bob
2565 /// = bob.userids().next().unwrap().userid().bind(
2566 /// &mut alice_signer, &bob,
2567 /// SignatureBuilder::new(SignatureType::GenericCertification))?;
2568 /// let bob = bob.insert_packets(vec![alice_certifies_bob.clone()])?.0;
2569 ///
2570 /// // Have Bob approve of that certification.
2571 /// let bobs_uid = bob.with_policy(policy, None)?.userids().next().unwrap();
2572 /// let approvals =
2573 /// bobs_uid.approve_of_certifications(
2574 /// &mut bob_signer,
2575 /// bobs_uid.certifications())?;
2576 /// let bob = bob.insert_packets(approvals)?.0;
2577 ///
2578 /// assert_eq!(bob.bad_signatures().count(), 0);
2579 /// assert_eq!(bob.userids().next().unwrap().certifications().next(),
2580 /// Some(&alice_certifies_bob));
2581 /// # Ok(()) }
2582 /// ```
2583 pub fn approve_of_certifications<C, S>(&self,
2584 primary_signer: &mut dyn Signer,
2585 certifications: C)
2586 -> Result<Vec<Signature>>
2587 where C: IntoIterator<Item = S>,
2588 S: Borrow<Signature>,
2589 {
2590 self.ca
2591 .approve_of_certifications(self.policy(),
2592 self.time(),
2593 primary_signer,
2594 certifications)
2595 }
2596}
2597
2598/// A Valid User Attribute and its associated data.
2599///
2600/// A specialized version of [`ValidComponentAmalgamation`].
2601///
2602pub type ValidUserAttributeAmalgamation<'a>
2603 = ValidComponentAmalgamation<'a, UserAttribute>;
2604
2605impl<'a> ValidUserAttributeAmalgamation<'a> {
2606 /// Returns a reference to the User Attribute.
2607 ///
2608 /// This is just a type-specific alias for
2609 /// [`ValidComponentAmalgamation::component`].
2610 ///
2611 /// # Examples
2612 ///
2613 /// ```
2614 /// # use sequoia_openpgp as openpgp;
2615 /// # use openpgp::cert::prelude::*;
2616 /// #
2617 /// # fn main() -> openpgp::Result<()> {
2618 /// # let (cert, _) =
2619 /// # CertBuilder::general_purpose(Some("alice@example.org"))
2620 /// # .generate()?;
2621 /// // Display some information about the User IDs.
2622 /// for ua in cert.user_attributes() {
2623 /// eprintln!(" - {:?}", ua.user_attribute());
2624 /// }
2625 /// # Ok(()) }
2626 /// ```
2627 pub fn user_attribute(&self) -> &'a UserAttribute {
2628 self.component()
2629 }
2630
2631 /// Returns the user attributes' approved third-party certifications.
2632 ///
2633 /// This feature is [experimental](crate#experimental-features).
2634 ///
2635 /// Allows the certificate owner to approve of third party
2636 /// certifications. See [Approved Certifications subpacket] for
2637 /// details. This can be used to address certificate flooding
2638 /// concerns.
2639 ///
2640 /// This method only returns signatures that are valid under the
2641 /// current policy and are approved by the certificate holder.
2642 ///
2643 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
2644 pub fn approved_certifications(&self)
2645 -> impl Iterator<Item=&Signature> + Send + Sync
2646 {
2647 let mut hash_algo = None;
2648 let digests: std::collections::HashSet<_> =
2649 self.certification_approval_key_signatures()
2650 .filter_map(|sig| {
2651 sig.approved_certifications().ok()
2652 .map(|digest_iter| (sig, digest_iter))
2653 })
2654 .flat_map(|(sig, digest_iter)| {
2655 hash_algo = Some(sig.hash_algo());
2656 digest_iter
2657 })
2658 .collect();
2659
2660 self.certifications()
2661 .filter_map(move |sig| {
2662 let mut hash = hash_algo.and_then(|a| a.context().ok())?
2663 .for_signature(sig.version());
2664 sig.hash_for_confirmation(&mut hash).ok()?;
2665 let digest = hash.into_digest().ok()?;
2666 if digests.contains(&digest[..]) {
2667 Some(sig)
2668 } else {
2669 None
2670 }
2671 })
2672 }
2673
2674 /// Returns set of active certification approval key signatures.
2675 ///
2676 /// This feature is [experimental](crate#experimental-features).
2677 ///
2678 /// Returns the set of signatures with the newest valid signature
2679 /// creation time. Older signatures are not returned. The sum of
2680 /// all digests in these signatures are the set of approved
2681 /// third-party certifications.
2682 ///
2683 /// This interface is useful for pruning old certification
2684 /// approval key signatures when filtering a certificate.
2685 ///
2686 /// Note: This is a low-level interface. Consider using
2687 /// [`ValidUserAttributeAmalgamation::approved_certifications`] to
2688 /// iterate over all approved certifications.
2689 ///
2690 /// [`ValidUserAttributeAmalgamation::approved_certifications`]: ValidUserAttributeAmalgamation#method.approved_certifications
2691 // The explicit link works around a bug in rustdoc.
2692 pub fn certification_approval_key_signatures(&'a self)
2693 -> impl Iterator<Item=&'a Signature> + Send + Sync
2694 {
2695 let mut first = None;
2696
2697 // The newest valid signature will be returned first.
2698 self.ca.approvals()
2699 // First, filter out any invalid (e.g. too new) signatures.
2700 .filter(move |sig| self.cert.policy().signature(
2701 sig,
2702 HashAlgoSecurity::CollisionResistance).is_ok())
2703 .take_while(move |sig| {
2704 let time_hash = (
2705 if let Some(t) = sig.signature_creation_time() {
2706 (t, sig.hash_algo())
2707 } else {
2708 // Something is off. Just stop.
2709 return false;
2710 },
2711 sig.hash_algo());
2712
2713 if let Some(reference) = first {
2714 // Stop looking once we see an older signature or one
2715 // with a different hash algo.
2716 reference == time_hash
2717 } else {
2718 first = Some(time_hash);
2719 true
2720 }
2721 })
2722 }
2723
2724 /// Approves of third-party certifications.
2725 ///
2726 /// This feature is [experimental](crate#experimental-features).
2727 ///
2728 /// Allows the certificate owner to approve of third party
2729 /// certifications. See [Approved Certifications subpacket] for
2730 /// details. This can be used to address certificate flooding
2731 /// concerns.
2732 ///
2733 /// [Approved Certifications subpacket]: https://www.ietf.org/archive/id/draft-dkg-openpgp-1pa3pc-02.html#approved-certifications-subpacket
2734 ///
2735 /// # Examples
2736 ///
2737 /// See [`ValidUserIDAmalgamation::approve_of_certifications#examples`].
2738 ///
2739 /// [`ValidUserIDAmalgamation::approve_of_certifications#examples`]: ValidUserIDAmalgamation#examples
2740 // The explicit link works around a bug in rustdoc.
2741 pub fn approve_of_certifications<C, S>(&self,
2742 primary_signer: &mut dyn Signer,
2743 certifications: C)
2744 -> Result<Vec<Signature>>
2745 where C: IntoIterator<Item = S>,
2746 S: Borrow<Signature>,
2747 {
2748 self.ca
2749 .approve_of_certifications(self.policy(),
2750 self.time(),
2751 primary_signer,
2752 certifications)
2753 }
2754}
2755
2756// derive(Clone) doesn't work with generic parameters that don't
2757// implement clone. But, we don't need to require that C implements
2758// Clone, because we're not cloning C, just the reference.
2759//
2760// See: https://github.com/rust-lang/rust/issues/26925
2761impl<'a, C> Clone for ValidComponentAmalgamation<'a, C> {
2762 fn clone(&self) -> Self {
2763 Self {
2764 ca: self.ca.clone(),
2765 cert: self.cert.clone(),
2766 binding_signature: self.binding_signature,
2767 }
2768 }
2769}
2770
2771impl<'a, C: 'a> From<ValidComponentAmalgamation<'a, C>>
2772 for ComponentAmalgamation<'a, C>
2773{
2774 fn from(vca: ValidComponentAmalgamation<'a, C>) -> Self {
2775 assert!(std::ptr::eq(vca.ca.cert(), vca.cert.cert()));
2776 vca.ca
2777 }
2778}
2779
2780impl<'a, C> ValidComponentAmalgamation<'a, C>
2781 where C: Ord + Send + Sync
2782{
2783 /// Returns the amalgamated primary component at time `time`
2784 ///
2785 /// If `time` is None, then the current time is used.
2786 /// `ValidComponentAmalgamationIter` for the definition of a valid component.
2787 ///
2788 /// The primary component is determined by taking the components that
2789 /// are alive at time `t`, and sorting them as follows:
2790 ///
2791 /// - non-revoked first
2792 /// - primary first
2793 /// - signature creation first
2794 ///
2795 /// If there is more than one, then one is selected in a
2796 /// deterministic, but undefined manner.
2797 ///
2798 /// If `valid_cert` is `false`, then this does not also check
2799 /// whether the certificate is valid; it only checks whether the
2800 /// component is valid. Normally, this should be `true`. This
2801 /// option is only exposed to allow breaking an infinite recursion:
2802 ///
2803 /// - To check if a certificate is valid, we check if the
2804 /// primary key is valid.
2805 ///
2806 /// - To check if the primary key is valid, we need the primary
2807 /// key's self signature
2808 ///
2809 /// - To find the primary key's self signature, we need to find
2810 /// the primary user id
2811 ///
2812 /// - To find the primary user id, we need to check if the user
2813 /// id is valid.
2814 ///
2815 /// - To check if the user id is valid, we need to check that
2816 /// the corresponding certificate is valid.
2817 pub(super) fn primary(cert: &'a Cert,
2818 iter: std::slice::Iter<'a, ComponentBundle<C>>,
2819 policy: &'a dyn Policy, t: SystemTime,
2820 valid_cert: bool)
2821 -> Result<ValidComponentAmalgamation<'a, C>>
2822 {
2823 use std::cmp::Ordering;
2824
2825 let mut error = None;
2826
2827 // Filter out components that are not alive at time `t`.
2828 //
2829 // While we have the binding signature, extract a few
2830 // properties to avoid recomputing the same thing multiple
2831 // times.
2832 iter.filter_map(|c| {
2833 // No binding signature at time `t` => not alive.
2834 let sig = match c.binding_signature(policy, t) {
2835 Ok(sig) => Some(sig),
2836 Err(e) => {
2837 error = Some(e);
2838 None
2839 },
2840 }?;
2841
2842 let revoked = c._revocation_status(policy, t, false, Some(sig));
2843 let primary = sig.primary_userid().unwrap_or(false);
2844 let signature_creation_time = match sig.signature_creation_time() {
2845 Some(time) => Some(time),
2846 None => {
2847 error = Some(Error::MalformedPacket(
2848 "Signature has no creation time".into()).into());
2849 None
2850 },
2851 }?;
2852
2853 Some(((c, sig, revoked), primary, signature_creation_time))
2854 })
2855 .max_by(|(a, a_primary, a_signature_creation_time),
2856 (b, b_primary, b_signature_creation_time)| {
2857 match (matches!(&a.2, RevocationStatus::Revoked(_)),
2858 matches!(&b.2, RevocationStatus::Revoked(_))) {
2859 (true, false) => return Ordering::Less,
2860 (false, true) => return Ordering::Greater,
2861 _ => (),
2862 }
2863 match (a_primary, b_primary) {
2864 (true, false) => return Ordering::Greater,
2865 (false, true) => return Ordering::Less,
2866 _ => (),
2867 }
2868 match a_signature_creation_time.cmp(b_signature_creation_time)
2869 {
2870 Ordering::Less => return Ordering::Less,
2871 Ordering::Greater => return Ordering::Greater,
2872 Ordering::Equal => (),
2873 }
2874
2875 // Fallback to a lexographical comparison. Prefer
2876 // the "smaller" one.
2877 match a.0.component().cmp(b.0.component()) {
2878 Ordering::Less => Ordering::Greater,
2879 Ordering::Greater => Ordering::Less,
2880 Ordering::Equal =>
2881 panic!("non-canonicalized Cert (duplicate components)"),
2882 }
2883 })
2884 .ok_or_else(|| {
2885 error.map(|e| e.context(format!(
2886 "No binding signature at time {}", crate::fmt::time(&t))))
2887 .unwrap_or_else(|| Error::NoBindingSignature(t).into())
2888 })
2889 .and_then(|c| ComponentAmalgamation::new(cert, (c.0).0)
2890 .with_policy_relaxed(policy, t, valid_cert))
2891 }
2892}
2893
2894impl<'a, C> seal::Sealed for ValidComponentAmalgamation<'a, C> {}
2895impl<'a, C> ValidateAmalgamation<'a, C> for ValidComponentAmalgamation<'a, C> {
2896 type V = Self;
2897
2898 fn with_policy<T>(&self, policy: &'a dyn Policy, time: T) -> Result<Self::V>
2899 where T: Into<Option<time::SystemTime>>,
2900 Self: Sized,
2901 {
2902 assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
2903
2904 let time = time.into().unwrap_or_else(crate::now);
2905 self.ca.with_policy(policy, time)
2906 }
2907}
2908
2909impl<'a, C> ValidAmalgamation<'a, C> for ValidComponentAmalgamation<'a, C>
2910where
2911 C: Send + Sync,
2912{
2913 fn valid_cert(&self) -> &ValidCert<'a> {
2914 assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
2915 &self.cert
2916 }
2917
2918 fn time(&self) -> SystemTime {
2919 assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
2920 self.cert.time
2921 }
2922
2923 fn policy(&self) -> &'a dyn Policy
2924 {
2925 assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
2926 self.cert.policy
2927 }
2928
2929 fn binding_signature(&self) -> &'a Signature {
2930 assert!(std::ptr::eq(self.ca.cert(), self.cert.cert()));
2931 self.binding_signature
2932 }
2933
2934 fn revocation_status(&self) -> RevocationStatus<'a> {
2935 self.bundle()._revocation_status(self.policy(), self.cert.time,
2936 false, Some(self.binding_signature))
2937 }
2938
2939 fn revocation_keys(&self)
2940 -> Box<dyn Iterator<Item = &'a RevocationKey> + 'a>
2941 {
2942 let mut keys = std::collections::HashSet::new();
2943
2944 let policy = self.policy();
2945 let pk_sec = self.cert().primary_key().key().hash_algo_security();
2946
2947 // All valid self-signatures.
2948 let sec = self.bundle().hash_algo_security;
2949 self.self_signatures()
2950 .filter(move |sig| {
2951 policy.signature(sig, sec).is_ok()
2952 })
2953 // All direct-key signatures.
2954 .chain(self.cert().primary_key()
2955 .self_signatures()
2956 .filter(|sig| {
2957 policy.signature(sig, pk_sec).is_ok()
2958 }))
2959 .flat_map(|sig| sig.revocation_keys())
2960 .for_each(|rk| { keys.insert(rk); });
2961
2962 Box::new(keys.into_iter())
2963 }
2964}
2965
2966impl<'a, C> ValidBindingSignature<'a, C> for ValidComponentAmalgamation<'a, C>
2967where
2968 C: Send + Sync,
2969{}
2970
2971impl<'a, C> crate::cert::Preferences<'a>
2972 for ValidComponentAmalgamation<'a, C>
2973where
2974 C: Send + Sync,
2975{
2976 fn preferred_symmetric_algorithms(&self)
2977 -> Option<&'a [SymmetricAlgorithm]> {
2978 self.map(|s| s.preferred_symmetric_algorithms())
2979 }
2980
2981 fn preferred_hash_algorithms(&self) -> Option<&'a [HashAlgorithm]> {
2982 self.map(|s| s.preferred_hash_algorithms())
2983 }
2984
2985 fn preferred_compression_algorithms(&self)
2986 -> Option<&'a [CompressionAlgorithm]> {
2987 self.map(|s| s.preferred_compression_algorithms())
2988 }
2989
2990 fn preferred_aead_ciphersuites(
2991 &self)
2992 -> Option<&'a [(SymmetricAlgorithm, AEADAlgorithm)]>
2993 {
2994 self.map(|s| s.preferred_aead_ciphersuites())
2995 }
2996
2997 fn key_server_preferences(&self) -> Option<KeyServerPreferences> {
2998 self.map(|s| s.key_server_preferences())
2999 }
3000
3001 fn preferred_key_server(&self) -> Option<&'a [u8]> {
3002 self.map(|s| s.preferred_key_server())
3003 }
3004
3005 fn policy_uri(&self) -> Option<&'a [u8]> {
3006 self.map(|s| s.policy_uri())
3007 }
3008
3009 fn features(&self) -> Option<Features> {
3010 self.map(|s| s.features())
3011 }
3012}
3013
3014#[cfg(test)]
3015mod test {
3016 use super::*;
3017
3018 use std::time::UNIX_EPOCH;
3019
3020 use crate::policy::StandardPolicy as P;
3021 use crate::Packet;
3022 use crate::packet::signature::SignatureBuilder;
3023 use crate::packet::UserID;
3024 use crate::types::SignatureType;
3025 use crate::types::ReasonForRevocation;
3026
3027 // derive(Clone) doesn't work with generic parameters that don't
3028 // implement clone. Make sure that our custom implementations
3029 // work.
3030 //
3031 // See: https://github.com/rust-lang/rust/issues/26925
3032 #[test]
3033 fn clone() {
3034 let p = &P::new();
3035
3036 let (cert, _) = CertBuilder::new()
3037 .add_userid("test@example.example")
3038 .generate()
3039 .unwrap();
3040
3041 let userid : UserIDAmalgamation = cert.userids().next().unwrap();
3042 assert_eq!(userid.userid(), userid.clone().userid());
3043
3044 let userid : ValidUserIDAmalgamation
3045 = userid.with_policy(p, None).unwrap();
3046 let c = userid.clone();
3047 assert_eq!(userid.userid(), c.userid());
3048 assert_eq!(userid.time(), c.time());
3049 }
3050
3051 #[test]
3052 fn map() {
3053 // The reference returned by `ComponentAmalgamation::userid`
3054 // and `ComponentAmalgamation::user_attribute` is bound by the
3055 // reference to the `Component` in the
3056 // `ComponentAmalgamation`, not the `ComponentAmalgamation`
3057 // itself.
3058 let (cert, _) = CertBuilder::new()
3059 .add_userid("test@example.example")
3060 .generate()
3061 .unwrap();
3062
3063 let _ = cert.userids().map(|ua| ua.userid())
3064 .collect::<Vec<_>>();
3065
3066 let _ = cert.user_attributes().map(|ua| ua.user_attribute())
3067 .collect::<Vec<_>>();
3068 }
3069
3070 #[test]
3071 fn component_amalgamation_certifications_by_key() -> Result<()> {
3072 // Alice and Bob certify Carol's certificate. We then check
3073 // that certifications_by_key returns them.
3074 let (alice, _) = CertBuilder::new()
3075 .add_userid("<alice@example.example>")
3076 .generate()
3077 .unwrap();
3078
3079 let (bob, _) = CertBuilder::new()
3080 .add_userid("<bob@example.example>")
3081 .generate()
3082 .unwrap();
3083
3084 let carol_userid = "<carol@example.example>";
3085 let (carol, _) = CertBuilder::new()
3086 .add_userid(carol_userid)
3087 .generate()
3088 .unwrap();
3089
3090 let ua = alice.userids().next().expect("have a user id");
3091 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
3092
3093 // Alice has not certified Bob's User ID.
3094 let ua = bob.userids().next().expect("have a user id");
3095 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
3096
3097 // Alice has not certified Carol's User ID.
3098 let ua = carol.userids().next().expect("have a user id");
3099 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 0);
3100
3101
3102 // Have Alice certify Carol's certificate.
3103 let mut alice_signer = alice.primary_key()
3104 .key()
3105 .clone()
3106 .parts_into_secret().expect("have unencrypted key material")
3107 .into_keypair().expect("have unencrypted key material");
3108 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3109 .sign_userid_binding(
3110 &mut alice_signer,
3111 carol.primary_key().key(),
3112 &UserID::from(carol_userid))?;
3113 let carol = carol.insert_packets(certification)?.0;
3114
3115 // Check that it is returned.
3116 let ua = carol.userids().next().expect("have a user id");
3117 assert_eq!(ua.certifications().count(), 1);
3118 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
3119 assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).count(), 0);
3120
3121
3122 // Have Bob certify Carol's certificate.
3123 let mut bob_signer = bob.primary_key()
3124 .key()
3125 .clone()
3126 .parts_into_secret().expect("have unencrypted key material")
3127 .into_keypair().expect("have unencrypted key material");
3128 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3129 .sign_userid_binding(
3130 &mut bob_signer,
3131 carol.primary_key().key(),
3132 &UserID::from(carol_userid))?;
3133 let carol = carol.insert_packets(certification)?.0;
3134
3135 // Check that it is returned.
3136 let ua = carol.userids().next().expect("have a user id");
3137 assert_eq!(ua.certifications().count(), 2);
3138 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
3139 assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).count(), 1);
3140
3141 // Again.
3142 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3143 .sign_userid_binding(
3144 &mut bob_signer,
3145 carol.primary_key().key(),
3146 &UserID::from(carol_userid))?;
3147 let carol = carol.insert_packets(certification)?.0;
3148
3149 // Check that it is returned.
3150 let ua = carol.userids().next().expect("have a user id");
3151 assert_eq!(ua.certifications().count(), 3);
3152 assert_eq!(ua.certifications_by_key(&[ alice.key_handle() ]).count(), 1);
3153 assert_eq!(ua.certifications_by_key(&[ bob.key_handle() ]).count(), 2);
3154
3155 Ok(())
3156 }
3157
3158 #[test]
3159 fn user_id_amalgamation_certifications_by_key() -> Result<()> {
3160 // Alice and Bob certify Carol's certificate. We then check
3161 // that valid_certifications_by_key and
3162 // active_certifications_by_key return them.
3163 let p = &crate::policy::StandardPolicy::new();
3164
3165 // $ date -u -d '2024-01-02 13:00' +%s
3166 let t0 = UNIX_EPOCH + Duration::new(1704200400, 0);
3167 // $ date -u -d '2024-01-02 14:00' +%s
3168 let t1 = UNIX_EPOCH + Duration::new(1704204000, 0);
3169 // $ date -u -d '2024-01-02 15:00' +%s
3170 let t2 = UNIX_EPOCH + Duration::new(1704207600, 0);
3171
3172 let (alice, _) = CertBuilder::new()
3173 .set_creation_time(t0)
3174 .add_userid("<alice@example.example>")
3175 .generate()
3176 .unwrap();
3177 let alice_primary = alice.primary_key().key();
3178
3179 let (bob, _) = CertBuilder::new()
3180 .set_creation_time(t0)
3181 .add_userid("<bob@example.example>")
3182 .generate()
3183 .unwrap();
3184 let bob_primary = bob.primary_key().key();
3185
3186 let carol_userid = "<carol@example.example>";
3187 let (carol, _) = CertBuilder::new()
3188 .set_creation_time(t0)
3189 .add_userid(carol_userid)
3190 .generate()
3191 .unwrap();
3192
3193 let ua = alice.userids().next().expect("have a user id");
3194 assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
3195 assert_eq!(ua.active_certifications_by_key(p, None, alice_primary).count(), 0);
3196
3197 // Alice has not certified Bob's User ID.
3198 let ua = bob.userids().next().expect("have a user id");
3199 assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
3200 assert_eq!(ua.active_certifications_by_key(p, None, alice_primary).count(), 0);
3201
3202 // Alice has not certified Carol's User ID.
3203 let ua = carol.userids().next().expect("have a user id");
3204 assert_eq!(ua.valid_certifications_by_key(p, None, alice_primary).count(), 0);
3205 assert_eq!(ua.active_certifications_by_key(p, None, alice_primary).count(), 0);
3206
3207
3208 // Have Alice certify Carol's certificate at t1.
3209 let mut alice_signer = alice_primary
3210 .clone()
3211 .parts_into_secret().expect("have unencrypted key material")
3212 .into_keypair().expect("have unencrypted key material");
3213 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3214 .set_signature_creation_time(t1)?
3215 .sign_userid_binding(
3216 &mut alice_signer,
3217 carol.primary_key().key(),
3218 &UserID::from(carol_userid))?;
3219 let carol = carol.insert_packets(certification.clone())?.0;
3220
3221 // Check that it is returned.
3222 let ua = carol.userids().next().expect("have a user id");
3223 assert_eq!(ua.certifications().count(), 1);
3224
3225 assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
3226 assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
3227
3228 assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 1);
3229 assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 1);
3230
3231 assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 0);
3232 assert_eq!(ua.active_certifications_by_key(p, t1, bob_primary).count(), 0);
3233
3234
3235 // Have Alice certify Carol's certificate at t1 (again).
3236 // Since both certifications were created at t1, they should
3237 // both be returned.
3238 let mut alice_signer = alice_primary
3239 .clone()
3240 .parts_into_secret().expect("have unencrypted key material")
3241 .into_keypair().expect("have unencrypted key material");
3242 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3243 .set_signature_creation_time(t1)?
3244 .sign_userid_binding(
3245 &mut alice_signer,
3246 carol.primary_key().key(),
3247 &UserID::from(carol_userid))?;
3248 let carol = carol.insert_packets(certification.clone())?.0;
3249
3250 // Check that it is returned.
3251 let ua = carol.userids().next().expect("have a user id");
3252 assert_eq!(ua.certifications().count(), 2);
3253 assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
3254 assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
3255
3256 assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
3257 assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
3258
3259 assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 2);
3260 assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 2);
3261
3262 assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
3263 assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
3264
3265
3266 // Have Alice certify Carol's certificate at t2. Now we only
3267 // have one active certification.
3268 let mut alice_signer = alice_primary
3269 .clone()
3270 .parts_into_secret().expect("have unencrypted key material")
3271 .into_keypair().expect("have unencrypted key material");
3272 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3273 .set_signature_creation_time(t2)?
3274 .sign_userid_binding(
3275 &mut alice_signer,
3276 carol.primary_key().key(),
3277 &UserID::from(carol_userid))?;
3278 let carol = carol.insert_packets(certification.clone())?.0;
3279
3280 // Check that it is returned.
3281 let ua = carol.userids().next().expect("have a user id");
3282 assert_eq!(ua.certifications().count(), 3);
3283 assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
3284 assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
3285
3286 assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
3287 assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
3288
3289 assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
3290 assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
3291
3292 assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
3293 assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
3294
3295
3296 // Have Bob certify Carol's certificate at t1 and have it expire at t2.
3297 let mut bob_signer = bob.primary_key()
3298 .key()
3299 .clone()
3300 .parts_into_secret().expect("have unencrypted key material")
3301 .into_keypair().expect("have unencrypted key material");
3302 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3303 .set_signature_creation_time(t1)?
3304 .set_signature_validity_period(t2.duration_since(t1)?)?
3305 .sign_userid_binding(
3306 &mut bob_signer,
3307 carol.primary_key().key(),
3308 &UserID::from(carol_userid))?;
3309 let carol = carol.insert_packets(certification.clone())?.0;
3310
3311 // Check that it is returned.
3312 let ua = carol.userids().next().expect("have a user id");
3313 assert_eq!(ua.certifications().count(), 4);
3314
3315 assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
3316 assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
3317
3318 assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
3319 assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
3320
3321 assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
3322 assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
3323
3324 assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
3325 assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
3326
3327 assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 1);
3328 assert_eq!(ua.active_certifications_by_key(p, t1, bob_primary).count(), 1);
3329
3330 // It expired.
3331 assert_eq!(ua.valid_certifications_by_key(p, t2, bob_primary).count(), 0);
3332 assert_eq!(ua.active_certifications_by_key(p, t2, bob_primary).count(), 0);
3333
3334
3335 // Have Bob certify Carol's certificate at t1 again. This
3336 // time don't have it expire.
3337 let mut bob_signer = bob.primary_key()
3338 .key()
3339 .clone()
3340 .parts_into_secret().expect("have unencrypted key material")
3341 .into_keypair().expect("have unencrypted key material");
3342 let certification = SignatureBuilder::new(SignatureType::GenericCertification)
3343 .set_signature_creation_time(t1)?
3344 .sign_userid_binding(
3345 &mut bob_signer,
3346 carol.primary_key().key(),
3347 &UserID::from(carol_userid))?;
3348 let carol = carol.insert_packets(certification.clone())?.0;
3349
3350 // Check that it is returned.
3351 let ua = carol.userids().next().expect("have a user id");
3352 assert_eq!(ua.certifications().count(), 5);
3353 assert_eq!(ua.valid_certifications_by_key(p, t0, alice_primary).count(), 0);
3354 assert_eq!(ua.active_certifications_by_key(p, t0, alice_primary).count(), 0);
3355
3356 assert_eq!(ua.valid_certifications_by_key(p, t1, alice_primary).count(), 2);
3357 assert_eq!(ua.active_certifications_by_key(p, t1, alice_primary).count(), 2);
3358
3359 assert_eq!(ua.valid_certifications_by_key(p, t2, alice_primary).count(), 3);
3360 assert_eq!(ua.active_certifications_by_key(p, t2, alice_primary).count(), 1);
3361
3362 assert_eq!(ua.valid_certifications_by_key(p, t0, bob_primary).count(), 0);
3363 assert_eq!(ua.active_certifications_by_key(p, t0, bob_primary).count(), 0);
3364
3365 assert_eq!(ua.valid_certifications_by_key(p, t1, bob_primary).count(), 2);
3366 assert_eq!(ua.active_certifications_by_key(p, t1, bob_primary).count(), 2);
3367
3368 // One of the certifications expired.
3369 assert_eq!(ua.valid_certifications_by_key(p, t2, bob_primary).count(), 1);
3370 assert_eq!(ua.active_certifications_by_key(p, t2, bob_primary).count(), 1);
3371
3372 Ok(())
3373 }
3374
3375 #[test]
3376 fn user_id_amalgamation_third_party_revocations_by_key() -> Result<()> {
3377 // Alice and Bob revoke Carol's User ID. We then check
3378 // that valid_third_party_revocations_by_key returns them.
3379 let p = &crate::policy::StandardPolicy::new();
3380
3381 // $ date -u -d '2024-01-02 13:00' +%s
3382 let t0 = UNIX_EPOCH + Duration::new(1704200400, 0);
3383 // $ date -u -d '2024-01-02 14:00' +%s
3384 let t1 = UNIX_EPOCH + Duration::new(1704204000, 0);
3385 // $ date -u -d '2024-01-02 15:00' +%s
3386 let t2 = UNIX_EPOCH + Duration::new(1704207600, 0);
3387
3388 let (alice, _) = CertBuilder::new()
3389 .set_creation_time(t0)
3390 .add_userid("<alice@example.example>")
3391 .generate()
3392 .unwrap();
3393 let alice_primary = alice.primary_key().key();
3394
3395 let (bob, _) = CertBuilder::new()
3396 .set_creation_time(t0)
3397 .add_userid("<bob@example.example>")
3398 .generate()
3399 .unwrap();
3400 let bob_primary = bob.primary_key().key();
3401
3402 let carol_userid = "<carol@example.example>";
3403 let (carol, _) = CertBuilder::new()
3404 .set_creation_time(t0)
3405 .add_userid(carol_userid)
3406 .generate()
3407 .unwrap();
3408 let carol_userid = UserID::from(carol_userid);
3409
3410 let ua = alice.userids().next().expect("have a user id");
3411 assert_eq!(ua.valid_third_party_revocations_by_key(p, None, alice_primary).count(), 0);
3412
3413 // Alice has not certified Bob's User ID.
3414 let ua = bob.userids().next().expect("have a user id");
3415 assert_eq!(ua.valid_third_party_revocations_by_key(p, None, alice_primary).count(), 0);
3416
3417 // Alice has not certified Carol's User ID.
3418 let ua = carol.userids().next().expect("have a user id");
3419 assert_eq!(ua.valid_third_party_revocations_by_key(p, None, alice_primary).count(), 0);
3420
3421
3422 // Have Alice revoke Carol's certificate at t1.
3423 let mut alice_signer = alice_primary
3424 .clone()
3425 .parts_into_secret().expect("have unencrypted key material")
3426 .into_keypair().expect("have unencrypted key material");
3427 let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
3428 .set_signature_creation_time(t1)?
3429 .set_reason_for_revocation(
3430 ReasonForRevocation::UIDRetired, b"")?
3431 .sign_userid_binding(
3432 &mut alice_signer,
3433 carol.primary_key().key(),
3434 &carol_userid)?;
3435 let carol = carol.insert_packets([
3436 Packet::from(carol_userid.clone()),
3437 Packet::from(certification.clone()),
3438 ])?.0;
3439
3440 // Check that it is returned.
3441 let ua = carol.userids().next().expect("have a user id");
3442 assert_eq!(ua.other_revocations().count(), 1);
3443
3444 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
3445 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 1);
3446 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, bob_primary).count(), 0);
3447
3448
3449 // Have Alice certify Carol's certificate at t1 (again).
3450 // Since both certifications were created at t1, they should
3451 // both be returned.
3452 let mut alice_signer = alice_primary
3453 .clone()
3454 .parts_into_secret().expect("have unencrypted key material")
3455 .into_keypair().expect("have unencrypted key material");
3456 let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
3457 .set_signature_creation_time(t1)?
3458 .set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
3459 .sign_userid_binding(
3460 &mut alice_signer,
3461 carol.primary_key().key(),
3462 &carol_userid)?;
3463 let carol = carol.insert_packets([
3464 Packet::from(carol_userid.clone()),
3465 Packet::from(certification.clone()),
3466 ])?.0;
3467
3468 // Check that it is returned.
3469 let ua = carol.userids().next().expect("have a user id");
3470 assert_eq!(ua.other_revocations().count(), 2);
3471 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
3472 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
3473 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 2);
3474 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
3475
3476
3477 // Have Alice certify Carol's certificate at t2. Now we only
3478 // have one active certification.
3479 let mut alice_signer = alice_primary
3480 .clone()
3481 .parts_into_secret().expect("have unencrypted key material")
3482 .into_keypair().expect("have unencrypted key material");
3483 let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
3484 .set_signature_creation_time(t2)?
3485 .set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
3486 .sign_userid_binding(
3487 &mut alice_signer,
3488 carol.primary_key().key(),
3489 &carol_userid)?;
3490 let carol = carol.insert_packets([
3491 Packet::from(carol_userid.clone()),
3492 Packet::from(certification.clone()),
3493 ])?.0;
3494
3495 // Check that it is returned.
3496 let ua = carol.userids().next().expect("have a user id");
3497 assert_eq!(ua.other_revocations().count(), 3);
3498 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
3499 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
3500 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
3501 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
3502
3503
3504 // Have Bob certify Carol's certificate at t1 and have it expire at t2.
3505 let mut bob_signer = bob.primary_key()
3506 .key()
3507 .clone()
3508 .parts_into_secret().expect("have unencrypted key material")
3509 .into_keypair().expect("have unencrypted key material");
3510 let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
3511 .set_signature_creation_time(t1)?
3512 .set_signature_validity_period(t2.duration_since(t1)?)?
3513 .set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
3514 .sign_userid_binding(
3515 &mut bob_signer,
3516 carol.primary_key().key(),
3517 &carol_userid)?;
3518 let carol = carol.insert_packets([
3519 Packet::from(carol_userid.clone()),
3520 Packet::from(certification.clone()),
3521 ])?.0;
3522
3523 // Check that it is returned.
3524 let ua = carol.userids().next().expect("have a user id");
3525 assert_eq!(ua.other_revocations().count(), 4);
3526
3527 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
3528 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
3529 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
3530 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
3531 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, bob_primary).count(), 1);
3532 // It expired.
3533 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, bob_primary).count(), 0);
3534
3535
3536 // Have Bob certify Carol's certificate at t1 again. This
3537 // time don't have it expire.
3538 let mut bob_signer = bob.primary_key()
3539 .key()
3540 .clone()
3541 .parts_into_secret().expect("have unencrypted key material")
3542 .into_keypair().expect("have unencrypted key material");
3543 let certification = SignatureBuilder::new(SignatureType::CertificationRevocation)
3544 .set_signature_creation_time(t1)?
3545 .set_reason_for_revocation(ReasonForRevocation::UIDRetired, b"")?
3546 .sign_userid_binding(
3547 &mut bob_signer,
3548 carol.primary_key().key(),
3549 &carol_userid)?;
3550 let carol = carol.insert_packets([
3551 Packet::from(carol_userid.clone()),
3552 Packet::from(certification.clone()),
3553 ])?.0;
3554
3555 // Check that it is returned.
3556 let ua = carol.userids().next().expect("have a user id");
3557 assert_eq!(ua.other_revocations().count(), 5);
3558 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, alice_primary).count(), 0);
3559 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, alice_primary).count(), 2);
3560 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, alice_primary).count(), 3);
3561 assert_eq!(ua.valid_third_party_revocations_by_key(p, t0, bob_primary).count(), 0);
3562 assert_eq!(ua.valid_third_party_revocations_by_key(p, t1, bob_primary).count(), 2);
3563 // One of the certifications expired.
3564 assert_eq!(ua.valid_third_party_revocations_by_key(p, t2, bob_primary).count(), 1);
3565
3566 Ok(())
3567 }
3568}