From 3db966f5c9b6857c5b3c3a2e9bc81bc80bc31f16 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sun, 13 Apr 2025 04:37:58 +0000 Subject: [PATCH 1/2] x509-cert: Create a `CrlNumber` from native types --- x509-cert/src/ext/pkix/crl.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/x509-cert/src/ext/pkix/crl.rs b/x509-cert/src/ext/pkix/crl.rs index a4011a5a9..33013e338 100644 --- a/x509-cert/src/ext/pkix/crl.rs +++ b/x509-cert/src/ext/pkix/crl.rs @@ -30,6 +30,22 @@ impl AssociatedOid for CrlNumber { impl_newtype!(CrlNumber, Uint); impl_extension!(CrlNumber, critical = false); +macro_rules! impl_from_traits { + ($($uint:ty),+) => { + $( + impl TryFrom<$uint> for CrlNumber { + type Error = der::Error; + + fn try_from(value: $uint) -> der::Result { + Uint::try_from(value).map(Self) + } + } + )+ + } +} + +impl_from_traits!(u8, u16, u32, u64, u128); + /// BaseCRLNumber as defined in [RFC 5280 Section 5.2.4]. /// /// ```text From 2353f2e72bc36c45a7f83ecc2df3a101e70c0bb9 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Sun, 13 Apr 2025 04:37:58 +0000 Subject: [PATCH 2/2] x509-cert: add a CRL builder --- x509-cert/src/builder.rs | 124 +++++++++++++++++- x509-cert/src/crl.rs | 8 ++ x509-cert/test-support/src/openssl.rs | 53 +++++++- x509-cert/tests/builder_crl.rs | 181 ++++++++++++++++++++++++++ 4 files changed, 361 insertions(+), 5 deletions(-) create mode 100644 x509-cert/tests/builder_crl.rs diff --git a/x509-cert/src/builder.rs b/x509-cert/src/builder.rs index 705a9797b..64f73683a 100644 --- a/x509-cert/src/builder.rs +++ b/x509-cert/src/builder.rs @@ -13,9 +13,13 @@ use spki::{ use crate::{ AlgorithmIdentifier, SubjectPublicKeyInfo, certificate::{Certificate, TbsCertificate, Version}, - ext::{AsExtension, Extensions}, + crl::{CertificateList, RevokedCert, TbsCertList}, + ext::{ + AsExtension, Extensions, + pkix::{AuthorityKeyIdentifier, CrlNumber, SubjectKeyIdentifier}, + }, serial_number::SerialNumber, - time::Validity, + time::{Time, Validity}, }; pub mod profile; @@ -411,3 +415,119 @@ where ::finalize(self, signer) } } + +/// X.509 CRL builder +pub struct CrlBuilder { + tbs: TbsCertList, +} + +impl CrlBuilder { + /// Create a `CrlBuilder` with the given issuer and the given monotonic [`CrlNumber`] + #[cfg(feature = "std")] + pub fn new(issuer: &Certificate, crl_number: CrlNumber) -> der::Result { + let this_update = Time::now()?; + Self::new_with_this_update(issuer, crl_number, this_update) + } + + /// Create a `CrlBuilder` with the given issuer, a given monotonic [`CrlNumber`], and valid + /// from the given `this_update` start validity date. + pub fn new_with_this_update( + issuer: &Certificate, + crl_number: CrlNumber, + this_update: Time, + ) -> der::Result { + // Replaced later when the finalize is called + let signature_alg = AlgorithmIdentifier { + oid: NULL_OID, + parameters: None, + }; + + let issuer_name = issuer.tbs_certificate.subject().clone(); + + let mut crl_extensions = Extensions::new(); + crl_extensions.push(crl_number.to_extension(&issuer_name, &crl_extensions)?); + let aki = match issuer + .tbs_certificate + .get_extension::()? + { + Some((_, aki)) => aki, + None => { + let ski = SubjectKeyIdentifier::try_from( + issuer + .tbs_certificate + .subject_public_key_info() + .owned_to_ref(), + )?; + AuthorityKeyIdentifier { + // KeyIdentifier must be the same as subjectKeyIdentifier + key_identifier: Some(ski.0.clone()), + // other fields must not be present. + ..Default::default() + } + } + }; + crl_extensions.push(aki.to_extension(&issuer_name, &crl_extensions)?); + + let tbs = TbsCertList { + version: Version::V2, + signature: signature_alg, + issuer: issuer_name, + this_update, + next_update: None, + revoked_certificates: None, + crl_extensions: Some(crl_extensions), + }; + + Ok(Self { tbs }) + } + + /// Make the CRL valid until the given `next_update` + pub fn with_next_update(mut self, next_update: Option