Skip to content

Commit

Permalink
Add certificate revocation (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
Moudoux authored Jan 29, 2024
1 parent a800b1b commit 8290a95
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ base64 = "0.21.0"
hyper = { version = "0.14.18", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.24", default-features = false, features = ["http1", "http2", "native-tokio", "tls12"], optional = true }
ring = { version = "0.17", features = ["std"] }
rustls-pki-types = "1.1.0"
serde = { version = "1.0.104", features = ["derive"] }
serde_json = "1.0.78"
thiserror = "1.0.30"
Expand Down
16 changes: 14 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use serde::Serialize;
mod types;
pub use types::{
AccountCredentials, Authorization, AuthorizationStatus, Challenge, ChallengeType, Error,
Identifier, LetsEncrypt, NewAccount, NewOrder, OrderState, OrderStatus, Problem, ZeroSsl,
Identifier, LetsEncrypt, NewAccount, NewOrder, OrderState, OrderStatus, Problem,
RevocationReason, RevocationRequest, ZeroSsl,
};
use types::{
DirectoryUrls, Empty, FinalizeRequest, Header, JoseJson, Jwk, KeyOrKeyId, NewAccountPayload,
Expand Down Expand Up @@ -362,6 +363,17 @@ impl Account {
url: order_url.ok_or("no order URL found")?,
})
}

/// Revokes a previously issued certificate
pub async fn revoke<'a>(&'a self, payload: &RevocationRequest<'a>) -> Result<(), Error> {
let rsp = self
.inner
.post(Some(payload), None, &self.inner.client.urls.revoke_cert)
.await?;
// The body is empty if the request was successful
let _ = Problem::from_response(rsp).await?;
Ok(())
}
}

struct AccountInner {
Expand Down Expand Up @@ -697,7 +709,7 @@ mod tests {

#[tokio::test]
async fn deserialize_old_credentials() -> Result<(), Error> {
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","urls":{"newNonce":"new-nonce","newAccount":"new-acct","newOrder":"new-order"}}"#;
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","urls":{"newNonce":"new-nonce","newAccount":"new-acct","newOrder":"new-order", "revokeCert": "revoke-cert"}}"#;
Account::from_credentials(serde_json::from_str::<AccountCredentials>(CREDENTIALS)?).await?;
Ok(())
}
Expand Down
49 changes: 49 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
use hyper::{Body, Response};
use ring::digest::{digest, Digest, SHA256};
use ring::signature::{EcdsaKeyPair, KeyPair};
use rustls_pki_types::CertificateDer;
use serde::de::DeserializeOwned;
use serde::ser::SerializeMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;

Expand Down Expand Up @@ -271,6 +273,52 @@ pub struct NewOrder<'a> {
pub identifiers: &'a [Identifier],
}

/// Payload for a certificate revocation request
/// Defined in <https://datatracker.ietf.org/doc/html/rfc8555#section-7.6>
#[derive(Debug)]
pub struct RevocationRequest<'a> {
/// The certificate to revoke
pub certificate: &'a CertificateDer<'a>,
/// Reason for revocation
pub reason: Option<RevocationReason>,
}

impl<'a> Serialize for RevocationRequest<'a> {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let base64 = BASE64_URL_SAFE_NO_PAD.encode(self.certificate);
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("certificate", &base64)?;
if let Some(reason) = &self.reason {
map.serialize_entry("reason", reason)?;
}
map.end()
}
}

/// The reason for a certificate revocation
/// Defined in <https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1>
#[allow(missing_docs)]
#[derive(Debug, Clone)]
#[repr(u8)]
pub enum RevocationReason {
Unspecified = 0,
KeyCompromise = 1,
CaCompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
RemoveFromCrl = 8,
PrivilegeWithdrawn = 9,
AaCompromise = 10,
}

impl Serialize for RevocationReason {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u8(self.clone() as u8)
}
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct NewAccountPayload<'a> {
Expand Down Expand Up @@ -302,6 +350,7 @@ pub(crate) struct DirectoryUrls {
pub(crate) new_nonce: String,
pub(crate) new_account: String,
pub(crate) new_order: String,
pub(crate) revoke_cert: String,
}

#[derive(Serialize)]
Expand Down

0 comments on commit 8290a95

Please sign in to comment.