x509: add minimal support for CRL building#2538
Conversation
|
It seems the CI overfailed with some HTTP 503 from GitHub, some of the cases need fixing though. |
I'm sorry, but I don't follow. An RFC 5280-compliant CRL only needs the AKI and the crlNumber per 5.2. I think those two should be enough, certainly for a first PR. |
|
Hi :)
Indeed. This pull request emerges from the fact that we implemented some CRL-related code in our company codebase out-of-tree, and since the upstream crate missed them and our hierarchy agreed to it, we figured we'd give some of the work back to the community. This is why we implemented more extensions than strictly necessary. We're happy to split the PR in multiple chunks if you prefer it this way. About some of the failing CIs because of Don't hesitate if you have any preference on how you want the contributions formatted ! |
|
Understood. Thanks a lot for upstreaming your changes. Indeed, splitting this up into smaller chunks will be most welcome. Then we can focus on correctness both on the standardization side and on the implementation side. As a first step, I would recommend you omit all the things that are not required to be in the And yes, for the CRL number you probably need to make a detour via a bignum and can therefore omit the addition of For the reason code, I think you can leave your approach but cast to |
|
Thanks for your answer !
To be sure, are you recommending we nuke our |
|
The ecosystem suffers quite a bit from non-compliant certificates and CRLs that overly flexible tools like the openssl command line let non-experts create. New code issuing certificates and CRLs should strive to be as standards-compliant as reasonably possible. I do not think it makes sense at all to issue a CRL that isn't version 2 in the present day and age, so I don't see the need for a I wold have created a |
|
Hi again, We're reworking the
Thanks. Edit: If we do need to construct errors, what is the idiomatic way doing that also ? e.g. for the |
19dda81 to
82ec4d4
Compare
82ec4d4 to
d4190a7
Compare
d4190a7 to
867f577
Compare
|
Hi, We saw that support for version 1.0.2 was removed, so we’ve updated the branch accordingly. Thanks (and happy new year by the way 🎉 ) ! |
|
Great, thank you. Apologies for the silence - it's been a crazy few weeks. I'll endeavour to give you feedback soon. |
botovq
left a comment
There was a problem hiding this comment.
I think we're getting quite close. I few small nits and one suggestion regarding empty list of revoked.
| number >= BigNum::from_u32(0)? && number < max, | ||
| "CrlNumber must have n ASN.1 integer greater than or equal to 0 and less than 2^159" |
There was a problem hiding this comment.
| number >= BigNum::from_u32(0)? && number < max, | |
| "CrlNumber must have n ASN.1 integer greater than or equal to 0 and less than 2^159" | |
| !number.is_negative() && number < max, | |
| "CrlNumber must be an ASN.1 integer greater than or equal to 0 and less than 2^159" |
| } | ||
| } | ||
|
|
||
| /// Set the lastUpdate time indicating when the CRL was issued. |
There was a problem hiding this comment.
lastUpdate is an OpenSSL-ism. The standard name is thisUpdate
| /// Set the lastUpdate time indicating when the CRL was issued. | |
| /// Set the lastUpdate (thisUpdate) time indicating when the CRL was issued. |
| unsafe { cvt(ffi::X509_CRL_set1_nextUpdate(self.0.as_ptr(), t.as_ptr())).map(|_| ()) } | ||
| } | ||
|
|
||
| /// Adds an X509 extension value to the CRL. |
There was a problem hiding this comment.
| /// Adds an X509 extension value to the CRL. | |
| /// Add an X509 extension value to the CRL. |
| self.append_extension2(&extension) | ||
| } | ||
|
|
||
| /// Adds an X509 extension value to the CRL. |
There was a problem hiding this comment.
| /// Adds an X509 extension value to the CRL. | |
| /// Add an X509 extension value to the CRL. |
| } | ||
|
|
||
| /// Consumes the builder, returning the `X509Revoked`. | ||
| pub fn build(self) -> X509Revoked { |
There was a problem hiding this comment.
I wonder if this should not refuse building an empty list of revoked certificates per RFC 5280, 5.1.2.6, "When there are no revoked certificates, the revoked certificates list MUST be absent."
a9ac7ac to
796cee4
Compare
|
Thank you for your review. I applied your suggestion, and added an other assert in the CrlBuilder regarding the empty revoked list. |
botovq
left a comment
There was a problem hiding this comment.
One last thing for you to fix. Then I believe we're done as far as code is concerned.
Before we can land this we will have to fix the min-version test either by pinning some crate (random?) or bumping the MSRV. I'll ask you to rebase once more once that's done.
| revoked.is_some_and(|r| !r.is_empty()), | ||
| "Revoked should not be empty or null" |
There was a problem hiding this comment.
| revoked.is_some_and(|r| !r.is_empty()), | |
| "Revoked should not be empty or null" | |
| // XXX - switch to is_none_or() once MSRV is 1.82. | |
| revoked.is_none() || revoked.is_some_and(|r| !r.is_empty()), | |
| "Revoked must be absent or non-empty" |
If we had MSRV 1.82, we could write this as revoked.is_none_or(|r| !r.is_empty()), but since we still have 1.70, this needs to be spelled out explicitly.
The reason is that the revokedCertificates element is OPTIONAL:
revokedCertificates SEQUENCE OF SEQUENCE {
userCertificate CertificateSerialNumber,
revocationDate Time,
crlEntryExtensions Extensions OPTIONAL
-- if present, version MUST be v2
} OPTIONAL,
per RFC 5280, section 5.1.2.6, which states: "When there are no revoked certificates, the revoked certificates list MUST be absent.", so the None case is valid.
796cee4 to
e6d615a
Compare
e6d615a to
d12e0d7
Compare
|
Hi, Sorry it took me a very long time to rebase the last changes. Everything seems OK here. |
d12e0d7 to
ca9620a
Compare
ca9620a to
4dae20b
Compare
botovq
left a comment
There was a problem hiding this comment.
Thanks for reviving this - I could have sworn I had merged this months ago... I reread the code and I am happy to merge once the CI is green.
If you wish to replace the asserts with the new ErrorStack::internal_error() you could do that as a follow-up.
Summary
This pull request introduces initial support for Certificate Revocation List (CRL) functionality. The current implementation of the library exposes many X.509 certificate features, but the APIs related to CRL creation and manipulation are still missing. This PR fills part of that gap by adding the essential types and extensions required to build and manage CRLs programmatically.
Motivation
The OpenSSL C API provides full support for generating, parsing, and extending CRLs. However, rust-openssl currently lacks high-level Rust bindings for these components. As a result, users cannot create CRLs, add revoked certificate entries, or include the standard CRL-related extensions expected in real-world PKI workflows. This PR aims to improve feature parity with OpenSSL and enable broader CRL use cases within Rust applications.
What’s Added
This contribution introduces wrappers and bindings for several key X.509 structures used in CRL creation:
X509RevokedBuilderAccessDescriptionBuilderDistributionPointBuilderDistributionPointNameBuilderAdditionally, the PR adds support for CRL extensions and CRL entry extensions, which are necessary for producing standards-compliant CRLs (e.g., CRL Number, Authority Key Identifier, CRL Reason Codes, Issuing Distribution Point).
Benefits
Enables programmatic creation and customization of CRLs in Rust
Improves parity with the OpenSSL CRL API
Helps developers build complete PKI workflows without relying on external tools