sequoia_openpgp/cert/
revoke.rs

1use std::convert::TryFrom;
2use std::time;
3
4use crate::{
5    HashAlgorithm,
6    Result,
7    SignatureType,
8};
9use crate::types::{
10    ReasonForRevocation,
11};
12use crate::crypto::Signer;
13use crate::packet::{
14    Key,
15    key,
16    signature,
17    Signature,
18    UserAttribute,
19    UserID,
20};
21use crate::packet::signature::subpacket::NotationDataFlags;
22use crate::cert::prelude::*;
23
24/// A builder for revocation certificates for OpenPGP certificates.
25///
26/// A revocation certificate for an OpenPGP certificate (as opposed
27/// to, say, a subkey) has two degrees of freedom: the certificate,
28/// and the key used to sign the revocation certificate.
29///
30/// Normally, the key used to sign the revocation certificate is the
31/// certificate's primary key.  However, this is not required.  For
32/// instance, if Alice has marked Robert's certificate (`R`) as a
33/// [designated revoker] for her certificate (`A`), then `R` can
34/// revoke `A` or parts of `A`.  In this case, the certificate is `A`,
35/// and the key used to sign the revocation certificate comes from
36/// `R`.
37///
38/// [designated revoker]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.23
39///
40/// # Examples
41///
42/// Revoke `cert`, which was compromised yesterday:
43///
44/// ```rust
45/// use sequoia_openpgp as openpgp;
46/// # use openpgp::Result;
47/// use openpgp::cert::prelude::*;
48/// use openpgp::policy::StandardPolicy;
49/// use openpgp::types::ReasonForRevocation;
50/// use openpgp::types::RevocationStatus;
51/// use openpgp::types::SignatureType;
52///
53/// # fn main() -> Result<()> {
54/// let p = &StandardPolicy::new();
55///
56/// # let (cert, _) = CertBuilder::new()
57/// #     .generate()?;
58/// # assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
59/// #            cert.revocation_status(p, None));
60/// #
61/// // Create and sign a revocation certificate.
62/// let mut signer = cert.primary_key().key().clone()
63///     .parts_into_secret()?.into_keypair()?;
64/// # let yesterday = std::time::SystemTime::now();
65/// let sig = CertRevocationBuilder::new()
66///     // Don't use the current time, since the certificate was
67///     // actually compromised yesterday.
68///     .set_signature_creation_time(yesterday)?
69///     .set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
70///                                b"It was the maid :/")?
71///     .build(&mut signer, &cert, None)?;
72///
73/// // Merge it into the certificate.
74/// let cert = cert.insert_packets(sig.clone())?.0;
75///
76/// // Now it's revoked.
77/// assert_eq!(RevocationStatus::Revoked(vec![&sig]),
78///            cert.revocation_status(p, None));
79/// # Ok(())
80/// # }
81pub struct CertRevocationBuilder {
82    builder: signature::SignatureBuilder,
83}
84assert_send_and_sync!(CertRevocationBuilder);
85
86impl CertRevocationBuilder {
87    /// Returns a new `CertRevocationBuilder`.
88    ///
89    /// # Examples
90    ///
91    /// ```rust
92    /// use sequoia_openpgp as openpgp;
93    /// # use openpgp::Result;
94    /// use openpgp::cert::prelude::*;
95    ///
96    /// # fn main() -> Result<()> {
97    /// let builder = CertRevocationBuilder::new();
98    /// # Ok(())
99    /// # }
100    pub fn new() -> Self {
101        Self {
102            builder:
103                signature::SignatureBuilder::new(SignatureType::KeyRevocation)
104        }
105    }
106
107    /// Sets the reason for revocation.
108    ///
109    /// # Examples
110    ///
111    /// ```rust
112    /// use sequoia_openpgp as openpgp;
113    /// # use openpgp::Result;
114    /// use openpgp::cert::prelude::*;
115    /// use openpgp::types::ReasonForRevocation;
116    ///
117    /// # fn main() -> Result<()> {
118    /// let builder = CertRevocationBuilder::new()
119    ///     .set_reason_for_revocation(ReasonForRevocation::KeyRetired,
120    ///                                b"I'm retiring this key.  \
121    ///                                  Please use my new OpenPGP certificate (FPR)");
122    /// # Ok(())
123    /// # }
124    pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
125                                     reason: &[u8])
126        -> Result<Self>
127    {
128        Ok(Self {
129            builder: self.builder.set_reason_for_revocation(code, reason)?
130        })
131    }
132
133    /// Sets the revocation certificate's creation time.
134    ///
135    /// The creation time is interpreted as the time at which the
136    /// certificate should be considered revoked.  For a soft
137    /// revocation, artifacts created prior to the revocation are
138    /// still considered valid.
139    ///
140    /// You'll usually want to set this explicitly and not use the
141    /// current time.
142    ///
143    /// First, the creation time should reflect the time of the event
144    /// that triggered the revocation.  As such, if it is discovered
145    /// that a certificate was compromised a week ago, then the
146    /// revocation certificate should be backdated appropriately.
147    ///
148    /// Second, because access to secret key material can be lost, it
149    /// can be useful to create a revocation certificate in advance.
150    /// Of course, such a revocation certificate will inevitably be
151    /// outdated.  To mitigate this problem, a number of revocation
152    /// certificates can be created with different creation times.
153    /// Then should a revocation certificate be needed, the most
154    /// appropriate one can be used.
155    ///
156    /// # Examples
157    ///
158    /// ```rust
159    /// use std::time::{SystemTime, Duration};
160    /// use sequoia_openpgp as openpgp;
161    /// # use openpgp::Result;
162    /// use openpgp::cert::prelude::*;
163    ///
164    /// # fn main() -> Result<()> {
165    /// let now = SystemTime::now();
166    /// let month = Duration::from_secs(((365.24 / 12.) * 24. * 60. * 60.) as u64);
167    ///
168    /// // Pre-generate revocation certificates, one for each month
169    /// // for the next 48 months.
170    /// for i in 0..48 {
171    ///     let builder = CertRevocationBuilder::new()
172    ///         .set_signature_creation_time(now + i * month);
173    ///     // ...
174    /// }
175    /// # Ok(())
176    /// # }
177    pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
178        -> Result<Self>
179    {
180        Ok(Self {
181            builder: self.builder.set_signature_creation_time(creation_time)?
182        })
183    }
184
185    /// Adds a notation to the revocation certificate.
186    ///
187    /// Unlike the [`CertRevocationBuilder::set_notation`] method, this function
188    /// does not first remove any existing notation with the specified name.
189    ///
190    /// See [`SignatureBuilder::add_notation`] for further documentation.
191    ///
192    /// [`SignatureBuilder::add_notation`]: crate::packet::signature::SignatureBuilder::add_notation()
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// use sequoia_openpgp as openpgp;
198    /// # use openpgp::Result;
199    /// use openpgp::cert::prelude::*;
200    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
201    ///
202    /// # fn main() -> Result<()> {
203    /// let builder = CertRevocationBuilder::new().add_notation(
204    ///     "revocation-policy@example.org",
205    ///     "https://policy.example.org/cert-revocation-policy",
206    ///     NotationDataFlags::empty().set_human_readable(),
207    ///     false,
208    /// );
209    /// # Ok(())
210    /// # }
211    pub fn add_notation<N, V, F>(self, name: N, value: V, flags: F,
212                                 critical: bool)
213        -> Result<Self>
214    where
215        N: AsRef<str>,
216        V: AsRef<[u8]>,
217        F: Into<Option<NotationDataFlags>>,
218    {
219        Ok(Self {
220            builder: self.builder.add_notation(name, value, flags, critical)?
221        })
222    }
223
224    /// Sets a notation to the revocation certificate.
225    ///
226    /// Unlike the [`CertRevocationBuilder::add_notation`] method, this function
227    /// first removes any existing notation with the specified name.
228    ///
229    /// See [`SignatureBuilder::set_notation`] for further documentation.
230    ///
231    /// [`SignatureBuilder::set_notation`]: crate::packet::signature::SignatureBuilder::set_notation()
232    ///
233    /// # Examples
234    ///
235    /// ```rust
236    /// use sequoia_openpgp as openpgp;
237    /// # use openpgp::Result;
238    /// use openpgp::cert::prelude::*;
239    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
240    ///
241    /// # fn main() -> Result<()> {
242    /// let builder = CertRevocationBuilder::new().set_notation(
243    ///     "revocation-policy@example.org",
244    ///     "https://policy.example.org/cert-revocation-policy",
245    ///     NotationDataFlags::empty().set_human_readable(),
246    ///     false,
247    /// );
248    /// # Ok(())
249    /// # }
250    pub fn set_notation<N, V, F>(self, name: N, value: V, flags: F,
251                                 critical: bool)
252        -> Result<Self>
253    where
254        N: AsRef<str>,
255        V: AsRef<[u8]>,
256        F: Into<Option<NotationDataFlags>>,
257    {
258        Ok(Self {
259            builder: self.builder.set_notation(name, value, flags, critical)?
260        })
261    }
262
263    /// Returns a signed revocation certificate.
264    ///
265    /// A revocation certificate is generated for `cert` and signed
266    /// using `signer` with the specified hash algorithm.  Normally,
267    /// you should pass `None` to select the default hash algorithm.
268    ///
269    /// # Examples
270    ///
271    /// ```rust
272    /// use sequoia_openpgp as openpgp;
273    /// # use openpgp::Result;
274    /// use openpgp::cert::prelude::*;
275    /// use openpgp::policy::StandardPolicy;
276    /// use openpgp::types::ReasonForRevocation;
277    /// # use openpgp::types::RevocationStatus;
278    /// # use openpgp::types::SignatureType;
279    ///
280    /// # fn main() -> Result<()> {
281    /// let p = &StandardPolicy::new();
282    ///
283    /// # let (cert, _) = CertBuilder::new()
284    /// #     .generate()?;
285    /// #
286    /// // Create and sign a revocation certificate.
287    /// let mut signer = cert.primary_key().key().clone()
288    ///     .parts_into_secret()?.into_keypair()?;
289    /// let sig = CertRevocationBuilder::new()
290    ///     .set_reason_for_revocation(ReasonForRevocation::KeyRetired,
291    ///                                b"Left Foo Corp.")?
292    ///     .build(&mut signer, &cert, None)?;
293    ///
294    /// # assert_eq!(sig.typ(), SignatureType::KeyRevocation);
295    /// #
296    /// # // Merge it into the certificate.
297    /// # let cert = cert.insert_packets(sig.clone())?.0;
298    /// #
299    /// # // Now it's revoked.
300    /// # assert_eq!(RevocationStatus::Revoked(vec![&sig]),
301    /// #            cert.revocation_status(p, None));
302    /// # Ok(())
303    /// # }
304    pub fn build<H>(self, signer: &mut dyn Signer, cert: &Cert, hash_algo: H)
305        -> Result<Signature>
306        where H: Into<Option<HashAlgorithm>>
307    {
308        self.builder
309            .set_hash_algo(hash_algo.into().unwrap_or(HashAlgorithm::SHA512))
310            .sign_direct_key(signer, cert.primary_key().key())
311    }
312}
313
314impl TryFrom<signature::SignatureBuilder> for CertRevocationBuilder {
315    type Error = anyhow::Error;
316
317    fn try_from(builder: signature::SignatureBuilder) -> Result<Self> {
318        if builder.typ() != SignatureType::KeyRevocation {
319            return Err(
320                crate::Error::InvalidArgument(
321                    format!("Expected signature type to be KeyRevocation but got {}",
322                            builder.typ())).into());
323        }
324        Ok(Self {
325            builder
326        })
327    }
328}
329
330/// A builder for revocation certificates for subkeys.
331///
332/// A revocation certificate for a subkey has three degrees of
333/// freedom: the certificate, the key used to generate the revocation
334/// certificate, and the subkey being revoked.
335///
336/// Normally, the key used to sign the revocation certificate is the
337/// certificate's primary key, and the subkey is a subkey that is
338/// bound to the certificate.  However, this is not required.  For
339/// instance, if Alice has marked Robert's certificate (`R`) as a
340/// [designated revoker] for her certificate (`A`), then `R` can
341/// revoke `A` or parts of `A`.  In such a case, the certificate is
342/// `A`, the key used to sign the revocation certificate comes from
343/// `R`, and the subkey being revoked is bound to `A`.
344///
345/// But, the subkey doesn't technically need to be bound to the
346/// certificate either.  For instance, it is technically possible for
347/// `R` to create a revocation certificate for a subkey in the context
348/// of `A`, even if that subkey is not bound to `A`.  Semantically,
349/// such a revocation certificate is currently meaningless.
350///
351/// [designated revoker]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.23
352///
353/// # Examples
354///
355/// Revoke a subkey, which is now considered to be too weak:
356///
357/// ```rust
358/// use sequoia_openpgp as openpgp;
359/// # use openpgp::Result;
360/// use openpgp::cert::prelude::*;
361/// use openpgp::policy::StandardPolicy;
362/// use openpgp::types::ReasonForRevocation;
363/// use openpgp::types::RevocationStatus;
364/// use openpgp::types::SignatureType;
365///
366/// # fn main() -> Result<()> {
367/// let p = &StandardPolicy::new();
368///
369/// # let (cert, _) = CertBuilder::new()
370/// #     .add_transport_encryption_subkey()
371/// #     .generate()?;
372/// # assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
373/// #            cert.revocation_status(p, None));
374/// #
375/// // Create and sign a revocation certificate.
376/// let mut signer = cert.primary_key().key().clone()
377///     .parts_into_secret()?.into_keypair()?;
378/// let subkey = cert.keys().subkeys().nth(0).unwrap();
379/// let sig = SubkeyRevocationBuilder::new()
380///     .set_reason_for_revocation(ReasonForRevocation::KeyRetired,
381///                                b"Revoking due to the recent crypto vulnerabilities.")?
382///     .build(&mut signer, &cert, subkey.key(), None)?;
383///
384/// // Merge it into the certificate.
385/// let cert = cert.insert_packets(sig.clone())?.0;
386///
387/// // Now it's revoked.
388/// let subkey = cert.keys().subkeys().nth(0).unwrap();
389/// if let RevocationStatus::Revoked(revocations) = subkey.revocation_status(p, None) {
390///     assert_eq!(revocations.len(), 1);
391///     assert_eq!(*revocations[0], sig);
392/// } else {
393///     panic!("Subkey is not revoked.");
394/// }
395///
396/// // But the certificate isn't.
397/// assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
398///            cert.revocation_status(p, None));
399/// # Ok(()) }
400/// ```
401pub struct SubkeyRevocationBuilder {
402    builder: signature::SignatureBuilder,
403}
404assert_send_and_sync!(SubkeyRevocationBuilder);
405
406impl SubkeyRevocationBuilder {
407    /// Returns a new `SubkeyRevocationBuilder`.
408    ///
409    /// # Examples
410    ///
411    /// ```rust
412    /// use sequoia_openpgp as openpgp;
413    /// # use openpgp::Result;
414    /// use openpgp::cert::prelude::*;
415    ///
416    /// # fn main() -> Result<()> {
417    /// let builder = SubkeyRevocationBuilder::new();
418    /// # Ok(())
419    /// # }
420    pub fn new() -> Self {
421        Self {
422            builder:
423                signature::SignatureBuilder::new(SignatureType::SubkeyRevocation)
424        }
425    }
426
427    /// Sets the reason for revocation.
428    ///
429    /// # Examples
430    ///
431    /// Revoke a possibly compromised subkey:
432    ///
433    /// ```rust
434    /// use sequoia_openpgp as openpgp;
435    /// # use openpgp::Result;
436    /// use openpgp::cert::prelude::*;
437    /// use openpgp::types::ReasonForRevocation;
438    ///
439    /// # fn main() -> Result<()> {
440    /// let builder = SubkeyRevocationBuilder::new()
441    ///     .set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
442    ///                                b"I lost my smartcard.");
443    /// # Ok(())
444    /// # }
445    pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
446                                     reason: &[u8])
447        -> Result<Self>
448    {
449        Ok(Self {
450            builder: self.builder.set_reason_for_revocation(code, reason)?
451        })
452    }
453
454    /// Sets the revocation certificate's creation time.
455    ///
456    /// The creation time is interpreted as the time at which the
457    /// subkey should be considered revoked.  For a soft revocation,
458    /// artifacts created prior to the revocation are still considered
459    /// valid.
460    ///
461    /// You'll usually want to set this explicitly and not use the
462    /// current time.  In particular, if a subkey is compromised,
463    /// you'll want to set this to the time when the compromise
464    /// happened.
465    ///
466    /// # Examples
467    ///
468    /// Create a revocation certificate for a subkey that was
469    /// compromised yesterday:
470    ///
471    /// ```rust
472    /// use sequoia_openpgp as openpgp;
473    /// # use openpgp::Result;
474    /// use openpgp::cert::prelude::*;
475    ///
476    /// # fn main() -> Result<()> {
477    /// # let yesterday = std::time::SystemTime::now();
478    /// let builder = SubkeyRevocationBuilder::new()
479    ///     .set_signature_creation_time(yesterday);
480    /// # Ok(())
481    /// # }
482    pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
483        -> Result<Self>
484    {
485        Ok(Self {
486            builder: self.builder.set_signature_creation_time(creation_time)?
487        })
488    }
489
490    /// Adds a notation to the revocation certificate.
491    ///
492    /// Unlike the [`SubkeyRevocationBuilder::set_notation`] method, this function
493    /// does not first remove any existing notation with the specified name.
494    ///
495    /// See [`SignatureBuilder::add_notation`] for further documentation.
496    ///
497    /// [`SignatureBuilder::add_notation`]: crate::packet::signature::SignatureBuilder::add_notation()
498    ///
499    /// # Examples
500    ///
501    /// ```rust
502    /// use sequoia_openpgp as openpgp;
503    /// # use openpgp::Result;
504    /// use openpgp::cert::prelude::*;
505    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
506    ///
507    /// # fn main() -> Result<()> {
508    /// let builder = CertRevocationBuilder::new().add_notation(
509    ///     "revocation-policy@example.org",
510    ///     "https://policy.example.org/cert-revocation-policy",
511    ///     NotationDataFlags::empty().set_human_readable(),
512    ///     false,
513    /// );
514    /// # Ok(())
515    /// # }
516    pub fn add_notation<N, V, F>(self, name: N, value: V, flags: F,
517                                 critical: bool)
518        -> Result<Self>
519    where
520        N: AsRef<str>,
521        V: AsRef<[u8]>,
522        F: Into<Option<NotationDataFlags>>,
523    {
524        Ok(Self {
525            builder: self.builder.add_notation(name, value, flags, critical)?
526        })
527    }
528
529    /// Sets a notation to the revocation certificate.
530    ///
531    /// Unlike the [`SubkeyRevocationBuilder::add_notation`] method, this function
532    /// first removes any existing notation with the specified name.
533    ///
534    /// See [`SignatureBuilder::set_notation`] for further documentation.
535    ///
536    /// [`SignatureBuilder::set_notation`]: crate::packet::signature::SignatureBuilder::set_notation()
537    ///
538    /// # Examples
539    ///
540    /// ```rust
541    /// use sequoia_openpgp as openpgp;
542    /// # use openpgp::Result;
543    /// use openpgp::cert::prelude::*;
544    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
545    ///
546    /// # fn main() -> Result<()> {
547    /// let builder = CertRevocationBuilder::new().set_notation(
548    ///     "revocation-policy@example.org",
549    ///     "https://policy.example.org/cert-revocation-policy",
550    ///     NotationDataFlags::empty().set_human_readable(),
551    ///     false,
552    /// );
553    /// # Ok(())
554    /// # }
555    pub fn set_notation<N, V, F>(self, name: N, value: V, flags: F,
556                                 critical: bool)
557        -> Result<Self>
558    where
559        N: AsRef<str>,
560        V: AsRef<[u8]>,
561        F: Into<Option<NotationDataFlags>>,
562    {
563        Ok(Self {
564            builder: self.builder.set_notation(name, value, flags, critical)?
565        })
566    }
567
568    /// Returns a signed revocation certificate.
569    ///
570    /// A revocation certificate is generated for `cert` and `key` and
571    /// signed using `signer` with the specified hash algorithm.
572    /// Normally, you should pass `None` to select the default hash
573    /// algorithm.
574    ///
575    /// # Examples
576    ///
577    /// Revoke a subkey, which is now considered to be too weak:
578    ///
579    /// ```rust
580    /// use sequoia_openpgp as openpgp;
581    /// # use openpgp::Result;
582    /// use openpgp::cert::prelude::*;
583    /// use openpgp::policy::StandardPolicy;
584    /// use openpgp::types::ReasonForRevocation;
585    /// # use openpgp::types::RevocationStatus;
586    /// # use openpgp::types::SignatureType;
587    ///
588    /// # fn main() -> Result<()> {
589    /// let p = &StandardPolicy::new();
590    ///
591    /// # let (cert, _) = CertBuilder::new()
592    /// #     .add_transport_encryption_subkey()
593    /// #     .generate()?;
594    /// #
595    /// // Create and sign a revocation certificate.
596    /// let mut signer = cert.primary_key().key().clone()
597    ///     .parts_into_secret()?.into_keypair()?;
598    /// let subkey = cert.keys().subkeys().nth(0).unwrap();
599    /// let sig = SubkeyRevocationBuilder::new()
600    ///     .set_reason_for_revocation(ReasonForRevocation::KeyRetired,
601    ///                                b"Revoking due to the recent crypto vulnerabilities.")?
602    ///     .build(&mut signer, &cert, subkey.key(), None)?;
603    ///
604    /// # assert_eq!(sig.typ(), SignatureType::SubkeyRevocation);
605    /// #
606    /// # // Merge it into the certificate.
607    /// # let cert = cert.insert_packets(sig.clone())?.0;
608    /// #
609    /// # // Now it's revoked.
610    /// # assert_eq!(RevocationStatus::Revoked(vec![&sig]),
611    /// #            cert.keys().subkeys().nth(0).unwrap().revocation_status(p, None));
612    /// # Ok(())
613    /// # }
614    pub fn build<H, P>(mut self, signer: &mut dyn Signer,
615                       cert: &Cert, key: &Key<P, key::SubordinateRole>,
616                       hash_algo: H)
617        -> Result<Signature>
618        where H: Into<Option<HashAlgorithm>>,
619              P: key::KeyParts,
620    {
621        self.builder = self.builder
622            .set_hash_algo(hash_algo.into().unwrap_or(HashAlgorithm::SHA512));
623
624        key.bind(signer, cert, self.builder)
625    }
626}
627
628impl TryFrom<signature::SignatureBuilder> for SubkeyRevocationBuilder {
629    type Error = anyhow::Error;
630
631    fn try_from(builder: signature::SignatureBuilder) -> Result<Self> {
632        if builder.typ() != SignatureType::SubkeyRevocation {
633            return Err(
634                crate::Error::InvalidArgument(
635                    format!("Expected signature type to be SubkeyRevocation but got {}",
636                            builder.typ())).into());
637        }
638        Ok(Self {
639            builder
640        })
641    }
642}
643
644/// A builder for revocation certificates for User ID.
645///
646/// A revocation certificate for a [User ID] has three degrees of
647/// freedom: the certificate, the key used to generate the revocation
648/// certificate, and the User ID being revoked.
649///
650/// Normally, the key used to sign the revocation certificate is the
651/// certificate's primary key, and the User ID is a User ID that is
652/// bound to the certificate.  However, this is not required.  For
653/// instance, if Alice has marked Robert's certificate (`R`) as a
654/// [designated revoker] for her certificate (`A`), then `R` can
655/// revoke `A` or parts of `A`.  In such a case, the certificate is
656/// `A`, the key used to sign the revocation certificate comes from
657/// `R`, and the User ID being revoked is bound to `A`.
658///
659/// But, the User ID doesn't technically need to be bound to the
660/// certificate either.  For instance, it is technically possible for
661/// `R` to create a revocation certificate for a User ID in the
662/// context of `A`, even if that User ID is not bound to `A`.
663/// Semantically, such a revocation certificate is currently
664/// meaningless.
665///
666/// [User ID]: crate::packet::UserID
667/// [designated revoker]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.23
668///
669/// # Examples
670///
671/// Revoke a User ID that is no longer valid:
672///
673/// ```rust
674/// use sequoia_openpgp as openpgp;
675/// # use openpgp::Result;
676/// use openpgp::cert::prelude::*;
677/// use openpgp::policy::StandardPolicy;
678/// use openpgp::types::ReasonForRevocation;
679/// use openpgp::types::RevocationStatus;
680/// use openpgp::types::SignatureType;
681///
682/// # fn main() -> Result<()> {
683/// let p = &StandardPolicy::new();
684///
685/// # let (cert, _) = CertBuilder::new()
686/// #     .add_userid("some@example.org")
687/// #     .generate()?;
688/// # assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
689/// #            cert.revocation_status(p, None));
690/// #
691/// // Create and sign a revocation certificate.
692/// let mut signer = cert.primary_key().key().clone()
693///     .parts_into_secret()?.into_keypair()?;
694/// let ua = cert.userids().nth(0).unwrap();
695/// let sig = UserIDRevocationBuilder::new()
696///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
697///                                b"Left example.org.")?
698///     .build(&mut signer, &cert, ua.userid(), None)?;
699///
700/// // Merge it into the certificate.
701/// let cert = cert.insert_packets(sig.clone())?.0;
702///
703/// // Now it's revoked.
704/// let ua = cert.userids().nth(0).unwrap();
705/// if let RevocationStatus::Revoked(revocations) = ua.revocation_status(p, None) {
706///     assert_eq!(revocations.len(), 1);
707///     assert_eq!(*revocations[0], sig);
708/// } else {
709///     panic!("User ID is not revoked.");
710/// }
711///
712/// // But the certificate isn't.
713/// assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
714///            cert.revocation_status(p, None));
715/// # Ok(()) }
716/// ```
717pub struct UserIDRevocationBuilder {
718    builder: signature::SignatureBuilder,
719}
720assert_send_and_sync!(UserIDRevocationBuilder);
721
722impl UserIDRevocationBuilder {
723    /// Returns a new `UserIDRevocationBuilder`.
724    ///
725    /// # Examples
726    ///
727    /// ```rust
728    /// use sequoia_openpgp as openpgp;
729    /// # use openpgp::Result;
730    /// use openpgp::cert::prelude::*;
731    ///
732    /// # fn main() -> Result<()> {
733    /// let builder = UserIDRevocationBuilder::new();
734    /// # Ok(())
735    /// # }
736    pub fn new() -> Self {
737        Self {
738            builder:
739                signature::SignatureBuilder::new(SignatureType::CertificationRevocation)
740        }
741    }
742
743    /// Sets the reason for revocation.
744    ///
745    /// Note: of the assigned reasons for revocation, only
746    /// [`ReasonForRevocation::UIDRetired`] is appropriate for User
747    /// IDs.  This parameter is not fixed, however, to allow the use
748    /// of the [private name space].
749    ///
750    /// [`ReasonForRevocation::UIDRetired`]: crate::types::ReasonForRevocation::UIDRetired
751    /// [private name space]: crate::types::ReasonForRevocation::Private
752    ///
753    ///
754    /// # Examples
755    ///
756    /// Revoke a User ID that is no longer valid:
757    ///
758    /// ```rust
759    /// use sequoia_openpgp as openpgp;
760    /// # use openpgp::Result;
761    /// use openpgp::cert::prelude::*;
762    /// use openpgp::types::ReasonForRevocation;
763    ///
764    /// # fn main() -> Result<()> {
765    /// let builder = UserIDRevocationBuilder::new()
766    ///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
767    ///                                b"Left example.org.");
768    /// # Ok(())
769    /// # }
770    pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
771                                     reason: &[u8])
772        -> Result<Self>
773    {
774        Ok(Self {
775            builder: self.builder.set_reason_for_revocation(code, reason)?
776        })
777    }
778
779    /// Sets the revocation certificate's creation time.
780    ///
781    /// The creation time is interpreted as the time at which the User
782    /// ID should be considered revoked.
783    ///
784    /// You'll usually want to set this explicitly and not use the
785    /// current time.  In particular, if a User ID is retired, you'll
786    /// want to set this to the time when the User ID was actually
787    /// retired.
788    ///
789    /// # Examples
790    ///
791    /// Create a revocation certificate for a User ID that was
792    /// retired yesterday:
793    ///
794    /// ```rust
795    /// use sequoia_openpgp as openpgp;
796    /// # use openpgp::Result;
797    /// use openpgp::cert::prelude::*;
798    ///
799    /// # fn main() -> Result<()> {
800    /// # let yesterday = std::time::SystemTime::now();
801    /// let builder = UserIDRevocationBuilder::new()
802    ///     .set_signature_creation_time(yesterday);
803    /// # Ok(())
804    /// # }
805    pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
806        -> Result<Self>
807    {
808        Ok(Self {
809            builder: self.builder.set_signature_creation_time(creation_time)?
810        })
811    }
812
813    /// Adds a notation to the revocation certificate.
814    ///
815    /// Unlike the [`UserIDRevocationBuilder::set_notation`] method, this function
816    /// does not first remove any existing notation with the specified name.
817    ///
818    /// See [`SignatureBuilder::add_notation`] for further documentation.
819    ///
820    /// [`SignatureBuilder::add_notation`]: crate::packet::signature::SignatureBuilder::add_notation()
821    ///
822    /// # Examples
823    ///
824    /// ```rust
825    /// use sequoia_openpgp as openpgp;
826    /// # use openpgp::Result;
827    /// use openpgp::cert::prelude::*;
828    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
829    ///
830    /// # fn main() -> Result<()> {
831    /// let builder = CertRevocationBuilder::new().add_notation(
832    ///     "revocation-policy@example.org",
833    ///     "https://policy.example.org/cert-revocation-policy",
834    ///     NotationDataFlags::empty().set_human_readable(),
835    ///     false,
836    /// );
837    /// # Ok(())
838    /// # }
839    pub fn add_notation<N, V, F>(self, name: N, value: V, flags: F,
840                                 critical: bool)
841        -> Result<Self>
842    where
843        N: AsRef<str>,
844        V: AsRef<[u8]>,
845        F: Into<Option<NotationDataFlags>>,
846    {
847        Ok(Self {
848            builder: self.builder.add_notation(name, value, flags, critical)?
849        })
850    }
851
852    /// Sets a notation to the revocation certificate.
853    ///
854    /// Unlike the [`UserIDRevocationBuilder::add_notation`] method, this function
855    /// first removes any existing notation with the specified name.
856    ///
857    /// See [`SignatureBuilder::set_notation`] for further documentation.
858    ///
859    /// [`SignatureBuilder::set_notation`]: crate::packet::signature::SignatureBuilder::set_notation()
860    ///
861    /// # Examples
862    ///
863    /// ```rust
864    /// use sequoia_openpgp as openpgp;
865    /// # use openpgp::Result;
866    /// use openpgp::cert::prelude::*;
867    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
868    ///
869    /// # fn main() -> Result<()> {
870    /// let builder = CertRevocationBuilder::new().set_notation(
871    ///     "revocation-policy@example.org",
872    ///     "https://policy.example.org/cert-revocation-policy",
873    ///     NotationDataFlags::empty().set_human_readable(),
874    ///     false,
875    /// );
876    /// # Ok(())
877    /// # }
878    pub fn set_notation<N, V, F>(self, name: N, value: V, flags: F,
879                                 critical: bool)
880        -> Result<Self>
881    where
882        N: AsRef<str>,
883        V: AsRef<[u8]>,
884        F: Into<Option<NotationDataFlags>>,
885    {
886        Ok(Self {
887            builder: self.builder.set_notation(name, value, flags, critical)?
888        })
889    }
890
891    /// Returns a signed revocation certificate.
892    ///
893    /// A revocation certificate is generated for `cert` and `userid`
894    /// and signed using `signer` with the specified hash algorithm.
895    /// Normally, you should pass `None` to select the default hash
896    /// algorithm.
897    ///
898    /// # Examples
899    ///
900    /// Revoke a User ID, because the user has left the organization:
901    ///
902    /// ```rust
903    /// use sequoia_openpgp as openpgp;
904    /// # use openpgp::Result;
905    /// use openpgp::cert::prelude::*;
906    /// use openpgp::policy::StandardPolicy;
907    /// use openpgp::types::ReasonForRevocation;
908    /// # use openpgp::types::RevocationStatus;
909    /// # use openpgp::types::SignatureType;
910    ///
911    /// # fn main() -> Result<()> {
912    /// let p = &StandardPolicy::new();
913    ///
914    /// # let (cert, _) = CertBuilder::new()
915    /// #     .add_userid("some@example.org")
916    /// #     .generate()?;
917    /// #
918    /// // Create and sign a revocation certificate.
919    /// let mut signer = cert.primary_key().key().clone()
920    ///     .parts_into_secret()?.into_keypair()?;
921    /// let ua = cert.userids().nth(0).unwrap();
922    /// let sig = UserIDRevocationBuilder::new()
923    ///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
924    ///                                b"Left example.org.")?
925    ///     .build(&mut signer, &cert, ua.userid(), None)?;
926    ///
927    /// # assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
928    /// #
929    /// # // Merge it into the certificate.
930    /// # let cert = cert.insert_packets(sig.clone())?.0;
931    /// #
932    /// # // Now it's revoked.
933    /// # assert_eq!(RevocationStatus::Revoked(vec![&sig]),
934    /// #            cert.userids().nth(0).unwrap().revocation_status(p, None));
935    /// # Ok(())
936    /// # }
937    pub fn build<H>(mut self, signer: &mut dyn Signer,
938                    cert: &Cert, userid: &UserID,
939                    hash_algo: H)
940        -> Result<Signature>
941        where H: Into<Option<HashAlgorithm>>
942    {
943        self.builder = self.builder
944            .set_hash_algo(hash_algo.into().unwrap_or(HashAlgorithm::SHA512));
945
946        userid.bind(signer, cert, self.builder)
947    }
948}
949
950impl TryFrom<signature::SignatureBuilder> for UserIDRevocationBuilder {
951    type Error = anyhow::Error;
952
953    fn try_from(builder: signature::SignatureBuilder) -> Result<Self> {
954        if builder.typ() != SignatureType::CertificationRevocation {
955            return Err(
956                crate::Error::InvalidArgument(
957                    format!("Expected signature type to be CertificationRevocation but got {}",
958                            builder.typ())).into());
959        }
960        Ok(Self {
961            builder
962        })
963    }
964}
965
966/// A builder for revocation certificates for User Attributes.
967///
968/// A revocation certificate for a [User Attribute] has three degrees of
969/// freedom: the certificate, the key used to generate the revocation
970/// certificate, and the User Attribute being revoked.
971///
972/// Normally, the key used to sign the revocation certificate is the
973/// certificate's primary key, and the User Attribute is a User
974/// Attribute that is bound to the certificate.  However, this is not
975/// required.  For instance, if Alice has marked Robert's certificate
976/// (`R`) as a [designated revoker] for her certificate (`A`), then
977/// `R` can revoke `A` or parts of `A`.  In such a case, the
978/// certificate is `A`, the key used to sign the revocation
979/// certificate comes from `R`, and the User Attribute being revoked
980/// is bound to `A`.
981///
982/// But, the User Attribute doesn't technically need to be bound to
983/// the certificate either.  For instance, it is technically possible
984/// for `R` to create a revocation certificate for a User Attribute in
985/// the context of `A`, even if that User Attribute is not bound to
986/// `A`.  Semantically, such a revocation certificate is currently
987/// meaningless.
988///
989/// [User Attribute]: crate::packet::user_attribute
990/// [designated revoker]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.23
991///
992/// # Examples
993///
994/// Revoke a User Attribute that is no longer valid:
995///
996/// ```rust
997/// # use openpgp::packet::user_attribute::Subpacket;
998/// use sequoia_openpgp as openpgp;
999/// # use openpgp::Result;
1000/// use openpgp::cert::prelude::*;
1001/// # use openpgp::packet::UserAttribute;
1002/// use openpgp::policy::StandardPolicy;
1003/// use openpgp::types::ReasonForRevocation;
1004/// use openpgp::types::RevocationStatus;
1005/// use openpgp::types::SignatureType;
1006///
1007/// # fn main() -> Result<()> {
1008/// let p = &StandardPolicy::new();
1009///
1010/// # // Create some user attribute. Doctests do not pass cfg(test),
1011/// # // so UserAttribute::arbitrary is not available
1012/// # let sp = Subpacket::Unknown(7, vec![7; 7].into_boxed_slice());
1013/// # let user_attribute = UserAttribute::new(&[sp])?;
1014/// #
1015/// # let (cert, _) = CertBuilder::new()
1016/// #     .add_user_attribute(user_attribute)
1017/// #     .generate()?;
1018/// # assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
1019/// #            cert.revocation_status(p, None));
1020/// #
1021/// // Create and sign a revocation certificate.
1022/// let mut signer = cert.primary_key().key().clone()
1023///     .parts_into_secret()?.into_keypair()?;
1024/// let ua = cert.user_attributes().nth(0).unwrap();
1025/// let sig = UserAttributeRevocationBuilder::new()
1026///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
1027///                                b"Lost the beard.")?
1028///     .build(&mut signer, &cert, ua.user_attribute(), None)?;
1029///
1030/// // Merge it into the certificate.
1031/// let cert = cert.insert_packets(sig.clone())?.0;
1032///
1033/// // Now it's revoked.
1034/// let ua = cert.user_attributes().nth(0).unwrap();
1035/// if let RevocationStatus::Revoked(revocations) = ua.revocation_status(p, None) {
1036///     assert_eq!(revocations.len(), 1);
1037///     assert_eq!(*revocations[0], sig);
1038/// } else {
1039///     panic!("User Attribute is not revoked.");
1040/// }
1041///
1042/// // But the certificate isn't.
1043/// assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
1044///            cert.revocation_status(p, None));
1045/// # Ok(()) }
1046/// ```
1047pub struct UserAttributeRevocationBuilder {
1048    builder: signature::SignatureBuilder,
1049}
1050assert_send_and_sync!(UserAttributeRevocationBuilder);
1051
1052impl UserAttributeRevocationBuilder {
1053    /// Returns a new `UserAttributeRevocationBuilder`.
1054    ///
1055    /// # Examples
1056    ///
1057    /// ```rust
1058    /// use sequoia_openpgp as openpgp;
1059    /// # use openpgp::Result;
1060    /// use openpgp::cert::prelude::*;
1061    ///
1062    /// # fn main() -> Result<()> {
1063    /// let builder = UserAttributeRevocationBuilder::new();
1064    /// # Ok(())
1065    /// # }
1066    pub fn new() -> Self {
1067        Self {
1068            builder:
1069                signature::SignatureBuilder::new(SignatureType::CertificationRevocation)
1070        }
1071    }
1072
1073    /// Sets the reason for revocation.
1074    ///
1075    /// Note: of the assigned reasons for revocation, only
1076    /// [`ReasonForRevocation::UIDRetired`] is appropriate for User
1077    /// Attributes.  This parameter is not fixed, however, to allow
1078    /// the use of the [private name space].
1079    ///
1080    /// [`ReasonForRevocation::UIDRetired`]: crate::types::ReasonForRevocation::UIDRetired
1081    /// [private name space]: crate::types::ReasonForRevocation::Private
1082    ///
1083    /// # Examples
1084    ///
1085    /// Revoke a User Attribute that is no longer valid:
1086    ///
1087    /// ```rust
1088    /// use sequoia_openpgp as openpgp;
1089    /// # use openpgp::Result;
1090    /// use openpgp::cert::prelude::*;
1091    /// use openpgp::types::ReasonForRevocation;
1092    ///
1093    /// # fn main() -> Result<()> {
1094    /// let builder = UserAttributeRevocationBuilder::new()
1095    ///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
1096    ///                                b"Lost the beard.");
1097    /// # Ok(())
1098    /// # }
1099    pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
1100                                     reason: &[u8])
1101        -> Result<Self>
1102    {
1103        Ok(Self {
1104            builder: self.builder.set_reason_for_revocation(code, reason)?
1105        })
1106    }
1107
1108    /// Sets the revocation certificate's creation time.
1109    ///
1110    /// The creation time is interpreted as the time at which the User
1111    /// Attribute should be considered revoked.
1112    ///
1113    /// You'll usually want to set this explicitly and not use the
1114    /// current time.  In particular, if a User Attribute is retired,
1115    /// you'll want to set this to the time when the User Attribute
1116    /// was actually retired.
1117    ///
1118    /// # Examples
1119    ///
1120    /// Create a revocation certificate for a User Attribute that was
1121    /// retired yesterday:
1122    ///
1123    /// ```rust
1124    /// use sequoia_openpgp as openpgp;
1125    /// # use openpgp::Result;
1126    /// use openpgp::cert::prelude::*;
1127    ///
1128    /// # fn main() -> Result<()> {
1129    /// # let yesterday = std::time::SystemTime::now();
1130    /// let builder = UserAttributeRevocationBuilder::new()
1131    ///     .set_signature_creation_time(yesterday);
1132    /// # Ok(())
1133    /// # }
1134    pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
1135        -> Result<Self>
1136    {
1137        Ok(Self {
1138            builder: self.builder.set_signature_creation_time(creation_time)?
1139        })
1140    }
1141
1142    /// Adds a notation to the revocation certificate.
1143    ///
1144    /// Unlike the [`UserAttributeRevocationBuilder::set_notation`] method, this function
1145    /// does not first remove any existing notation with the specified name.
1146    ///
1147    /// See [`SignatureBuilder::add_notation`] for further documentation.
1148    ///
1149    /// [`SignatureBuilder::add_notation`]: crate::packet::signature::SignatureBuilder::add_notation()
1150    ///
1151    /// # Examples
1152    ///
1153    /// ```rust
1154    /// use sequoia_openpgp as openpgp;
1155    /// # use openpgp::Result;
1156    /// use openpgp::cert::prelude::*;
1157    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
1158    ///
1159    /// # fn main() -> Result<()> {
1160    /// let builder = CertRevocationBuilder::new().add_notation(
1161    ///     "revocation-policy@example.org",
1162    ///     "https://policy.example.org/cert-revocation-policy",
1163    ///     NotationDataFlags::empty().set_human_readable(),
1164    ///     false,
1165    /// );
1166    /// # Ok(())
1167    /// # }
1168    pub fn add_notation<N, V, F>(self, name: N, value: V, flags: F,
1169                                 critical: bool)
1170        -> Result<Self>
1171    where
1172        N: AsRef<str>,
1173        V: AsRef<[u8]>,
1174        F: Into<Option<NotationDataFlags>>,
1175    {
1176        Ok(Self {
1177            builder: self.builder.add_notation(name, value, flags, critical)?
1178        })
1179    }
1180
1181    /// Sets a notation to the revocation certificate.
1182    ///
1183    /// Unlike the [`UserAttributeRevocationBuilder::add_notation`] method, this function
1184    /// first removes any existing notation with the specified name.
1185    ///
1186    /// See [`SignatureBuilder::set_notation`] for further documentation.
1187    ///
1188    /// [`SignatureBuilder::set_notation`]: crate::packet::signature::SignatureBuilder::set_notation()
1189    ///
1190    /// # Examples
1191    ///
1192    /// ```rust
1193    /// use sequoia_openpgp as openpgp;
1194    /// # use openpgp::Result;
1195    /// use openpgp::cert::prelude::*;
1196    /// use openpgp::packet::signature::subpacket::NotationDataFlags;
1197    ///
1198    /// # fn main() -> Result<()> {
1199    /// let builder = CertRevocationBuilder::new().set_notation(
1200    ///     "revocation-policy@example.org",
1201    ///     "https://policy.example.org/cert-revocation-policy",
1202    ///     NotationDataFlags::empty().set_human_readable(),
1203    ///     false,
1204    /// );
1205    /// # Ok(())
1206    /// # }
1207    pub fn set_notation<N, V, F>(self, name: N, value: V, flags: F,
1208                                 critical: bool)
1209        -> Result<Self>
1210    where
1211        N: AsRef<str>,
1212        V: AsRef<[u8]>,
1213        F: Into<Option<NotationDataFlags>>,
1214    {
1215        Ok(Self {
1216            builder: self.builder.set_notation(name, value, flags, critical)?
1217        })
1218    }
1219
1220    /// Returns a signed revocation certificate.
1221    ///
1222    /// A revocation certificate is generated for `cert` and `ua` and
1223    /// signed using `signer` with the specified hash algorithm.
1224    /// Normally, you should pass `None` to select the default hash
1225    /// algorithm.
1226    ///
1227    /// # Examples
1228    ///
1229    /// Revoke a User Attribute, because the identity is no longer
1230    /// valid:
1231    ///
1232    /// ```rust
1233    /// # use openpgp::packet::user_attribute::Subpacket;
1234    /// use sequoia_openpgp as openpgp;
1235    /// # use openpgp::Result;
1236    /// use openpgp::cert::prelude::*;
1237    /// # use openpgp::packet::UserAttribute;
1238    /// use openpgp::policy::StandardPolicy;
1239    /// use openpgp::types::ReasonForRevocation;
1240    /// # use openpgp::types::RevocationStatus;
1241    /// # use openpgp::types::SignatureType;
1242    ///
1243    /// # fn main() -> Result<()> {
1244    /// let p = &StandardPolicy::new();
1245    ///
1246    /// # // Create some user attribute. Doctests do not pass cfg(test),
1247    /// # // so UserAttribute::arbitrary is not available
1248    /// # let sp = Subpacket::Unknown(7, vec![7; 7].into_boxed_slice());
1249    /// # let user_attribute = UserAttribute::new(&[sp])?;
1250    /// #
1251    /// # let (cert, _) = CertBuilder::new()
1252    /// #     .add_user_attribute(user_attribute)
1253    /// #     .generate()?;
1254    /// // Create and sign a revocation certificate.
1255    /// let mut signer = cert.primary_key().key().clone()
1256    ///     .parts_into_secret()?.into_keypair()?;
1257    /// let ua = cert.user_attributes().nth(0).unwrap();
1258    /// let sig = UserAttributeRevocationBuilder::new()
1259    ///     .set_reason_for_revocation(ReasonForRevocation::UIDRetired,
1260    ///                                b"Lost the beard.")?
1261    ///     .build(&mut signer, &cert, ua.user_attribute(), None)?;
1262    ///
1263    /// # assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
1264    /// #
1265    /// # // Merge it into the certificate.
1266    /// # let cert = cert.insert_packets(sig.clone())?.0;
1267    /// #
1268    /// # // Now it's revoked.
1269    /// # assert_eq!(RevocationStatus::Revoked(vec![&sig]),
1270    /// #            cert.user_attributes().nth(0).unwrap().revocation_status(p, None));
1271    /// # Ok(())
1272    /// # }
1273    pub fn build<H>(mut self, signer: &mut dyn Signer,
1274                    cert: &Cert, ua: &UserAttribute,
1275                    hash_algo: H)
1276        -> Result<Signature>
1277        where H: Into<Option<HashAlgorithm>>
1278    {
1279        self.builder = self.builder
1280            .set_hash_algo(hash_algo.into().unwrap_or(HashAlgorithm::SHA512));
1281
1282        ua.bind(signer, cert, self.builder)
1283    }
1284}
1285
1286impl TryFrom<signature::SignatureBuilder> for UserAttributeRevocationBuilder {
1287    type Error = anyhow::Error;
1288
1289    fn try_from(builder: signature::SignatureBuilder) -> Result<Self> {
1290        if builder.typ() != SignatureType::CertificationRevocation {
1291            return Err(
1292                crate::Error::InvalidArgument(
1293                    format!("Expected signature type to be CertificationRevocation but got {}",
1294                            builder.typ())).into());
1295        }
1296        Ok(Self {
1297            builder
1298        })
1299    }
1300}
1301
1302#[cfg(test)]
1303mod tests {
1304    #[test]
1305    fn try_into_cert_revocation_builder_success() -> crate::Result<()> {
1306        use std::convert::TryInto;
1307        use crate as openpgp;
1308        use openpgp::cert::prelude::*;
1309        use openpgp::packet::signature::SignatureBuilder;
1310        use openpgp::cert::CertRevocationBuilder;
1311        use openpgp::types::SignatureType;
1312
1313        let (cert, _) = CertBuilder::new()
1314            .generate()?;
1315
1316        // Create and sign a revocation certificate.
1317        let mut signer = cert.primary_key().key().clone()
1318            .parts_into_secret()?.into_keypair()?;
1319        let builder = SignatureBuilder::new(SignatureType::KeyRevocation);
1320        let revocation_builder: CertRevocationBuilder = builder.try_into()?;
1321        let sig = revocation_builder.build(&mut signer, &cert, None)?;
1322        assert_eq!(sig.typ(), SignatureType::KeyRevocation);
1323        Ok(())
1324    }
1325
1326    #[test]
1327    fn try_into_cert_revocation_builder_failure() -> crate::Result<()> {
1328        use std::convert::TryInto;
1329        use crate as openpgp;
1330        use openpgp::packet::signature::SignatureBuilder;
1331        use openpgp::cert::CertRevocationBuilder;
1332        use openpgp::types::SignatureType;
1333
1334        let builder = SignatureBuilder::new(SignatureType::Binary);
1335        let result: openpgp::Result<CertRevocationBuilder> = builder.try_into();
1336        assert!(result.is_err());
1337        Ok(())
1338    }
1339
1340    #[test]
1341    fn try_into_subkey_revocation_builder_success() -> crate::Result<()> {
1342        use std::convert::TryInto;
1343        use crate as openpgp;
1344        use openpgp::cert::prelude::*;
1345        use openpgp::packet::signature::SignatureBuilder;
1346        use openpgp::cert::SubkeyRevocationBuilder;
1347        use openpgp::types::SignatureType;
1348
1349        let (cert, _) = CertBuilder::new()
1350            .add_transport_encryption_subkey()
1351            .generate()?;
1352
1353        // Create and sign a revocation certificate.
1354        let mut signer = cert.primary_key().key().clone()
1355            .parts_into_secret()?.into_keypair()?;
1356        let subkey = cert.keys().subkeys().nth(0).unwrap();
1357        let builder = SignatureBuilder::new(SignatureType::SubkeyRevocation);
1358        let revocation_builder: SubkeyRevocationBuilder = builder.try_into()?;
1359        let sig = revocation_builder.build(&mut signer, &cert, subkey.key(), None)?;
1360        assert_eq!(sig.typ(), SignatureType::SubkeyRevocation);
1361        Ok(())
1362    }
1363
1364    #[test]
1365    fn try_into_subkey_revocation_builder_failure() -> crate::Result<()> {
1366        use std::convert::TryInto;
1367        use crate as openpgp;
1368        use openpgp::packet::signature::SignatureBuilder;
1369        use openpgp::cert::SubkeyRevocationBuilder;
1370        use openpgp::types::SignatureType;
1371
1372        let builder = SignatureBuilder::new(SignatureType::Binary);
1373        let result: openpgp::Result<SubkeyRevocationBuilder> = builder.try_into();
1374        assert!(result.is_err());
1375        Ok(())
1376    }
1377
1378    #[test]
1379    fn try_into_userid_revocation_builder_success() -> crate::Result<()> {
1380        use std::convert::TryInto;
1381        use crate as openpgp;
1382        use openpgp::cert::prelude::*;
1383        use openpgp::packet::signature::SignatureBuilder;
1384        use openpgp::cert::UserIDRevocationBuilder;
1385        use openpgp::types::SignatureType;
1386
1387        let (cert, _) = CertBuilder::new()
1388            .add_userid("test@example.com")
1389            .generate()?;
1390
1391        // Create and sign a revocation certificate.
1392        let mut signer = cert.primary_key().key().clone()
1393            .parts_into_secret()?.into_keypair()?;
1394        let user_id = cert.userids().next().unwrap().userid();
1395        let builder = SignatureBuilder::new(SignatureType::CertificationRevocation);
1396        let revocation_builder: UserIDRevocationBuilder = builder.try_into()?;
1397        let sig = revocation_builder.build(&mut signer, &cert, user_id, None)?;
1398        assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
1399        Ok(())
1400    }
1401
1402    #[test]
1403    fn try_into_userid_revocation_builder_failure() -> crate::Result<()> {
1404        use std::convert::TryInto;
1405        use crate as openpgp;
1406        use openpgp::packet::signature::SignatureBuilder;
1407        use openpgp::cert::UserIDRevocationBuilder;
1408        use openpgp::types::SignatureType;
1409
1410        let builder = SignatureBuilder::new(SignatureType::Binary);
1411        let result: openpgp::Result<UserIDRevocationBuilder> = builder.try_into();
1412        assert!(result.is_err());
1413        Ok(())
1414    }
1415
1416    #[test]
1417    fn try_into_userattribute_revocation_builder_success() -> crate::Result<()> {
1418        use std::convert::TryInto;
1419        use crate as openpgp;
1420        use openpgp::cert::prelude::*;
1421        use openpgp::packet::prelude::*;
1422        use openpgp::packet::signature::SignatureBuilder;
1423        use openpgp::packet::user_attribute::Subpacket;
1424        use openpgp::cert::UserAttributeRevocationBuilder;
1425        use openpgp::types::SignatureType;
1426
1427        let sp = Subpacket::Unknown(7, vec![7; 7].into_boxed_slice());
1428        let user_attribute = UserAttribute::new(&[sp])?;
1429
1430        let (cert, _) = CertBuilder::new()
1431            .add_user_attribute(user_attribute)
1432            .generate()?;
1433
1434        // Create and sign a revocation certificate.
1435        let mut signer = cert.primary_key().key().clone()
1436            .parts_into_secret()?.into_keypair()?;
1437        let user_attribute =
1438            cert.user_attributes().next().unwrap().user_attribute();
1439        let builder = SignatureBuilder::new(SignatureType::CertificationRevocation);
1440        let revocation_builder: UserAttributeRevocationBuilder = builder.try_into()?;
1441        let sig = revocation_builder.build(&mut signer, &cert, user_attribute, None)?;
1442        assert_eq!(sig.typ(), SignatureType::CertificationRevocation);
1443        Ok(())
1444    }
1445
1446    #[test]
1447    fn try_into_userattribute_revocation_builder_failure() -> crate::Result<()> {
1448        use std::convert::TryInto;
1449        use crate as openpgp;
1450        use openpgp::packet::signature::SignatureBuilder;
1451        use openpgp::cert::UserAttributeRevocationBuilder;
1452        use openpgp::types::SignatureType;
1453
1454        let builder = SignatureBuilder::new(SignatureType::Binary);
1455        let result: openpgp::Result<UserAttributeRevocationBuilder> = builder.try_into();
1456        assert!(result.is_err());
1457        Ok(())
1458    }
1459}