summaryrefslogtreecommitdiffstats
path: root/openpgp/src/cert/revoke.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/cert/revoke.rs')
-rw-r--r--openpgp/src/cert/revoke.rs485
1 files changed, 485 insertions, 0 deletions
diff --git a/openpgp/src/cert/revoke.rs b/openpgp/src/cert/revoke.rs
new file mode 100644
index 00000000..ba3b6c6e
--- /dev/null
+++ b/openpgp/src/cert/revoke.rs
@@ -0,0 +1,485 @@
+use std::ops::Deref;
+use std::time;
+
+use crate::{
+ conversions::Time,
+ HashAlgorithm,
+ Result,
+ SignatureType,
+};
+use crate::types::{
+ ReasonForRevocation,
+};
+use crate::crypto::hash::Hash;
+use crate::crypto::Signer;
+use crate::packet::{
+ key,
+ signature,
+ Signature,
+ UserAttribute,
+ UserID,
+};
+use crate::cert::Cert;
+
+/// A `Cert` revocation builder.
+///
+/// Note: a Cert revocation has two degrees of freedom: the Cert, and
+/// the key used to generate the revocation.
+///
+/// Normally, the key used to generate the revocation is the Cert's
+/// primary key. However, this is not required.
+///
+/// If Alice has marked Robert's key (R) as a designated revoker
+/// for her key (A), then R can revoke A or parts of A. In this
+/// case, the Cert is A, and the key used to generate the
+/// revocation comes from R.
+///
+/// # Example
+///
+/// ```rust
+/// # extern crate sequoia_openpgp as openpgp;
+/// # use openpgp::Result;
+/// use openpgp::RevocationStatus;
+/// use openpgp::types::{ReasonForRevocation, SignatureType};
+/// use openpgp::cert::{CipherSuite, CertBuilder, CertRevocationBuilder};
+/// use openpgp::crypto::KeyPair;
+/// use openpgp::parse::Parse;
+///
+/// # fn main() { f().unwrap(); }
+/// # fn f() -> Result<()>
+/// # {
+/// let (cert, _) = CertBuilder::new()
+/// .set_cipher_suite(CipherSuite::Cv25519)
+/// .generate()?;
+/// assert_eq!(RevocationStatus::NotAsFarAsWeKnow,
+/// cert.revoked(None));
+///
+/// let mut signer = cert.primary().clone()
+/// .mark_parts_secret()?.into_keypair()?;
+/// let sig = CertRevocationBuilder::new()
+/// .set_reason_for_revocation(ReasonForRevocation::KeyCompromised,
+/// b"It was the maid :/")?
+/// .build(&mut signer, &cert, None)?;
+/// assert_eq!(sig.typ(), SignatureType::KeyRevocation);
+///
+/// let cert = cert.merge_packets(vec![sig.clone().into()])?;
+/// assert_eq!(RevocationStatus::Revoked(vec![&sig]),
+/// cert.revoked(None));
+/// # Ok(())
+/// # }
+pub struct CertRevocationBuilder {
+ builder: signature::Builder,
+}
+
+impl CertRevocationBuilder {
+ /// Returns a new `CertRevocationBuilder`.
+ pub fn new() -> Self {
+ Self {
+ builder:
+ signature::Builder::new(SignatureType::KeyRevocation)
+ }
+ }
+
+ /// Sets the reason for revocation.
+ pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
+ reason: &[u8])
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_reason_for_revocation(code, reason)?
+ })
+ }
+
+ /// Sets the revocation signature's creation time.
+ pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_signature_creation_time(creation_time)?
+ })
+ }
+
+ /// Returns a revocation certificate for the cert `Cert` signed by
+ /// `signer`.
+ pub fn build<H, R>(self, signer: &mut dyn Signer<R>, cert: &Cert, hash_algo: H)
+ -> Result<Signature>
+ where H: Into<Option<HashAlgorithm>>,
+ R: key::KeyRole
+ {
+ let hash_algo = hash_algo.into().unwrap_or(HashAlgorithm::SHA512);
+ let mut hash = hash_algo.context()?;
+
+ cert.primary().hash(&mut hash);
+
+ let creation_time
+ = self.signature_creation_time()
+ .unwrap_or_else(|| time::SystemTime::now().canonicalize());
+
+ self.builder
+ // If not set, set it to now.
+ .set_signature_creation_time(creation_time)?
+ .set_issuer_fingerprint(signer.public().fingerprint())?
+ .set_issuer(signer.public().keyid())?
+ .sign_hash(signer, hash_algo, hash)
+ }
+}
+
+impl Deref for CertRevocationBuilder {
+ type Target = signature::Builder;
+
+ fn deref(&self) -> &Self::Target {
+ &self.builder
+ }
+}
+
+
+/// A `Subkey` revocation builder.
+///
+/// Note: this function has three degrees of freedom: the Cert, the
+/// key used to generate the revocation, and the subkey.
+///
+/// Normally, the key used to generate the revocation is the Cert's
+/// primary key, and the subkey is a subkey that is bound to the
+/// Cert. However, this is not required.
+///
+/// If Alice has marked Robert's key (R) as a designated revoker
+/// for her key (A), then R can revoke A or parts of A. In this
+/// case, the Cert is A, the key used to generate the revocation
+/// comes from R, and the User ID is bound to A.
+///
+/// But, the component doesn't technically need to be bound to the
+/// Cert. For instance, it is possible for R to revoke the User ID
+/// "bob@example.org" in the context of A, even if
+/// "bob@example.org" is not bound to A.
+///
+/// # Example
+///
+/// ```
+/// # use sequoia_openpgp::{*, packet::*, types::*, cert::*};
+/// # f().unwrap();
+/// # fn f() -> Result<()> {
+/// // Generate a Cert, and create a keypair from the primary key.
+/// let (cert, _) = CertBuilder::new()
+/// .add_encryption_subkey()
+/// .generate()?;
+/// let mut keypair = cert.primary().clone()
+/// .mark_parts_secret()?.into_keypair()?;
+/// let subkey = cert.subkeys().nth(0).unwrap();
+///
+/// // Generate the revocation for the first and only Subkey.
+/// let revocation =
+/// SubkeyRevocationBuilder::new()
+/// .set_reason_for_revocation(
+/// ReasonForRevocation::KeyRetired,
+/// b"Smells funny.").unwrap()
+/// .build(&mut keypair, &cert, subkey.key(), None)?;
+/// assert_eq!(revocation.typ(), SignatureType::SubkeyRevocation);
+///
+/// // Now merge the revocation signature into the Cert.
+/// let cert = cert.merge_packets(vec![revocation.clone().into()])?;
+///
+/// // Check that it is revoked.
+/// let subkey = cert.subkeys().nth(0).unwrap();
+/// if let RevocationStatus::Revoked(revocations) = subkey.revoked(None) {
+/// assert_eq!(revocations.len(), 1);
+/// assert_eq!(*revocations[0], revocation);
+/// } else {
+/// panic!("Subkey is not revoked.");
+/// }
+/// # Ok(()) }
+/// ```
+pub struct SubkeyRevocationBuilder {
+ builder: signature::Builder,
+}
+
+impl SubkeyRevocationBuilder {
+ /// Returns a new `SubkeyRevocationBuilder`.
+ pub fn new() -> Self {
+ Self {
+ builder:
+ signature::Builder::new(SignatureType::SubkeyRevocation)
+ }
+ }
+
+ /// Sets the reason for revocation.
+ pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
+ reason: &[u8])
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_reason_for_revocation(code, reason)?
+ })
+ }
+
+ /// Sets the revocation signature's creation time.
+ pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_signature_creation_time(creation_time)?
+ })
+ }
+
+ /// Returns a revocation certificate for the cert `Cert` signed by
+ /// `signer`.
+ pub fn build<H, R>(mut self, signer: &mut dyn Signer<R>,
+ cert: &Cert, key: &key::PublicSubkey,
+ hash_algo: H)
+ -> Result<Signature>
+ where H: Into<Option<HashAlgorithm>>,
+ R: key::KeyRole
+ {
+ let hash_algo = hash_algo.into().unwrap_or(HashAlgorithm::SHA512);
+ let creation_time
+ = self.signature_creation_time()
+ .unwrap_or_else(|| time::SystemTime::now().canonicalize());
+
+ if let Some(algo) = hash_algo.into() {
+ self.builder = self.builder.set_hash_algo(algo);
+ }
+ key.bind(signer, cert, self.builder, creation_time)
+ }
+}
+
+impl Deref for SubkeyRevocationBuilder {
+ type Target = signature::Builder;
+
+ fn deref(&self) -> &Self::Target {
+ &self.builder
+ }
+}
+
+/// A `UserID` revocation builder.
+///
+/// Note: this function has three degrees of freedom: the Cert, the
+/// key used to generate the revocation, and the user id.
+///
+/// Normally, the key used to generate the revocation is the Cert's
+/// primary key, and the user id is a user id that is bound to the
+/// Cert. However, this is not required.
+///
+/// If Alice has marked Robert's key (R) as a designated revoker
+/// for her key (A), then R can revoke A or parts of A. In this
+/// case, the Cert is A, the key used to generate the revocation
+/// comes from R, and the User ID is bound to A.
+///
+/// But, the component doesn't technically need to be bound to the
+/// Cert. For instance, it is possible for R to revoke the User ID
+/// "bob@example.org" in the context of A, even if
+/// "bob@example.org" is not bound to A.
+///
+/// # Example
+///
+/// ```
+/// # use sequoia_openpgp::{*, packet::*, types::*, cert::*};
+/// # f().unwrap();
+/// # fn f() -> Result<()> {
+/// // Generate a Cert, and create a keypair from the primary key.
+/// let (cert, _) = CertBuilder::new()
+/// .add_userid("some@example.org")
+/// .generate()?;
+/// let mut keypair = cert.primary().clone()
+/// .mark_parts_secret()?.into_keypair()?;
+/// let userid = cert.userids().nth(0).unwrap();
+///
+/// // Generate the revocation for the first and only UserID.
+/// let revocation =
+/// UserIDRevocationBuilder::new()
+/// .set_reason_for_revocation(
+/// ReasonForRevocation::KeyRetired,
+/// b"Left example.org.").unwrap()
+/// .build(&mut keypair, &cert, userid.userid(), None)?;
+/// assert_eq!(revocation.typ(), SignatureType::CertificateRevocation);
+///
+/// // Now merge the revocation signature into the Cert.
+/// let cert = cert.merge_packets(vec![revocation.clone().into()])?;
+///
+/// // Check that it is revoked.
+/// let userid = cert.userids().nth(0).unwrap();
+/// if let RevocationStatus::Revoked(revocations) = userid.revoked(None) {
+/// assert_eq!(revocations.len(), 1);
+/// assert_eq!(*revocations[0], revocation);
+/// } else {
+/// panic!("UserID is not revoked.");
+/// }
+/// # Ok(()) }
+/// ```
+pub struct UserIDRevocationBuilder {
+ builder: signature::Builder,
+}
+
+impl UserIDRevocationBuilder {
+ /// Returns a new `UserIDRevocationBuilder`.
+ pub fn new() -> Self {
+ Self {
+ builder:
+ signature::Builder::new(SignatureType::CertificateRevocation)
+ }
+ }
+
+ /// Sets the reason for revocation.
+ pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
+ reason: &[u8])
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_reason_for_revocation(code, reason)?
+ })
+ }
+
+ /// Sets the revocation signature's creation time.
+ pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_signature_creation_time(creation_time)?
+ })
+ }
+
+ /// Returns a revocation certificate for the cert `Cert` signed by
+ /// `signer`.
+ pub fn build<H, R>(mut self, signer: &mut dyn Signer<R>,
+ cert: &Cert, userid: &UserID,
+ hash_algo: H)
+ -> Result<Signature>
+ where H: Into<Option<HashAlgorithm>>,
+ R: key::KeyRole
+ {
+ let hash_algo = hash_algo.into().unwrap_or(HashAlgorithm::SHA512);
+ let creation_time
+ = self.signature_creation_time()
+ .unwrap_or_else(|| time::SystemTime::now().canonicalize());
+
+ if let Some(algo) = hash_algo.into() {
+ self.builder = self.builder.set_hash_algo(algo);
+ }
+ userid.bind(signer, cert, self.builder, creation_time)
+ }
+}
+
+impl Deref for UserIDRevocationBuilder {
+ type Target = signature::Builder;
+
+ fn deref(&self) -> &Self::Target {
+ &self.builder
+ }
+}
+
+/// A `UserAttribute` revocation builder.
+///
+/// Note: this function has three degrees of freedom: the Cert, the
+/// key used to generate the revocation, and the user attribute.
+///
+/// Normally, the key used to generate the revocation is the Cert's
+/// primary key, and the user attribute is a user attribute that is
+/// bound to the Cert. However, this is not required.
+///
+/// If Alice has marked Robert's key (R) as a designated revoker
+/// for her key (A), then R can revoke A or parts of A. In this
+/// case, the Cert is A, the key used to generate the revocation
+/// comes from R, and the User Attribute is bound to A.
+///
+/// But, the component doesn't technically need to be bound to the
+/// Cert. For instance, it is possible for R to revoke the User ID
+/// "bob@example.org" in the context of A, even if
+/// "bob@example.org" is not bound to A.
+///
+/// # Example
+///
+/// ```
+/// # use sequoia_openpgp::{*, packet::*, types::*, cert::*};
+/// # f().unwrap();
+/// # fn f() -> Result<()> {
+/// # let subpacket
+/// # = user_attribute::Subpacket::Unknown(1, [ 1 ].to_vec().into_boxed_slice());
+/// # let some_user_attribute = UserAttribute::new(&[ subpacket ])?;
+/// // Generate a Cert, and create a keypair from the primary key.
+/// let (cert, _) = CertBuilder::new()
+/// .add_user_attribute(some_user_attribute)
+/// .generate()?;
+/// let mut keypair = cert.primary().clone()
+/// .mark_parts_secret()?.into_keypair()?;
+/// let ua = cert.user_attributes().nth(0).unwrap();
+///
+/// // Generate the revocation for the first and only UserAttribute.
+/// let revocation =
+/// UserAttributeRevocationBuilder::new()
+/// .set_reason_for_revocation(
+/// ReasonForRevocation::KeyRetired,
+/// b"Left example.org.").unwrap()
+/// .build(&mut keypair, &cert, ua.user_attribute(), None)?;
+/// assert_eq!(revocation.typ(), SignatureType::CertificateRevocation);
+///
+/// // Now merge the revocation signature into the Cert.
+/// let cert = cert.merge_packets(vec![revocation.clone().into()])?;
+///
+/// // Check that it is revoked.
+/// let ua = cert.user_attributes().nth(0).unwrap();
+/// if let RevocationStatus::Revoked(revocations) = ua.revoked(None) {
+/// assert_eq!(revocations.len(), 1);
+/// assert_eq!(*revocations[0], revocation);
+/// } else {
+/// panic!("UserAttribute is not revoked.");
+/// }
+/// # Ok(()) }
+/// ```
+pub struct UserAttributeRevocationBuilder {
+ builder: signature::Builder,
+}
+
+impl UserAttributeRevocationBuilder {
+ /// Returns a new `UserAttributeRevocationBuilder`.
+ pub fn new() -> Self {
+ Self {
+ builder:
+ signature::Builder::new(SignatureType::CertificateRevocation)
+ }
+ }
+
+ /// Sets the reason for revocation.
+ pub fn set_reason_for_revocation(self, code: ReasonForRevocation,
+ reason: &[u8])
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_reason_for_revocation(code, reason)?
+ })
+ }
+
+ /// Sets the revocation signature's creation time.
+ pub fn set_signature_creation_time(self, creation_time: time::SystemTime)
+ -> Result<Self>
+ {
+ Ok(Self {
+ builder: self.builder.set_signature_creation_time(creation_time)?
+ })
+ }
+
+ /// Returns a revocation certificate for the cert `Cert` signed by
+ /// `signer`.
+ pub fn build<H, R>(mut self, signer: &mut dyn Signer<R>,
+ cert: &Cert, ua: &UserAttribute,
+ hash_algo: H)
+ -> Result<Signature>
+ where H: Into<Option<HashAlgorithm>>,
+ R: key::KeyRole
+ {
+ let hash_algo = hash_algo.into().unwrap_or(HashAlgorithm::SHA512);
+ let creation_time
+ = self.signature_creation_time()
+ .unwrap_or_else(|| time::SystemTime::now().canonicalize());
+
+ if let Some(algo) = hash_algo.into() {
+ self.builder = self.builder.set_hash_algo(algo);
+ }
+ ua.bind(signer, cert, self.builder, creation_time)
+ }
+}
+
+impl Deref for UserAttributeRevocationBuilder {
+ type Target = signature::Builder;
+
+ fn deref(&self) -> &Self::Target {
+ &self.builder
+ }
+}