Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CRL handling #77

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open

Add CRL handling #77

wants to merge 32 commits into from

Conversation

tristanmiller-spruceid
Copy link

I haven't had a lot of success finding test vectors that exactly math the mdoc spec. Here's my stab at a first pass.

src/definitions/x509/crl.rs Outdated Show resolved Hide resolved

/// Given a cert, download and verify the associated crl listed in the cert, and verify the cert
/// against the crl
pub async fn fetch_and_validate_crl(cert: &TbsCertificate) -> Result<(), Error> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the CRL isn't necessarily signed by the PK in the cert. For 18013-5 the IACA root cert is the signer for all CRLs in the certificate chain, so you might need to update this function to take in a "crl_signer_cert" parameter.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tried a slightly different strategy since there are other certs that need to be checked, but we don't want to have to download the CRL multiple times.

I therefore separated fetch_and_validate_crl from check_cert_against cert_lists, allowing higher level code with more context of the cert tree to iterate on that structure, fetching CRL and validating it against the root cert, then separately using that CRL against any other certs it sees fit.

Does that work for you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is quite correct. From my understanding it is possible for a child certificate to have a different CRL endpoint than the root. Perhaps I'm wrong on that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justAnIdentity do you know?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed "root_cert" to "crl_signing_cert" to hopefully alleviate these concerns.

src/definitions/x509/crl.rs Show resolved Hide resolved
}

async fn fetch_crl(url: &str) -> Result<Vec<u8>, Error> {
let bytes = reqwest::get(url)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to create a CrlVerifier struct to avoid re-creating the reqwest client every time

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm don't believe that matches our data access pattern. AFAIK, there isn't really a case where we need to batch many if even multiple requests for CRLs.

SignatureInWrongFormat,
}

pub const OID_EC_CURVE_P256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This library should burden itself with defining mappings. It should instead rely on the pkcs8 crate with the AssociatedOid trait. Then the verification function can accept an object that implements both DecodePublicKey and the Verifier trait from the signature trait -- or something along these lines, maybe DynAssociatedAlgorithmIdentifier will be useful. Then it's on the consumer to pick the "crypto backend". And it allows to build an object that can support multiple types of keys.

A "default" verifier can be provided by this library, but feature gated.

uris
}
DistributionPointName::NameRelativeToCRLIssuer(_) => Err(
Error::DistributionPointMalformed("contained relative to issuer name"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using a NameRelativeToCRLIssuer is allowed according to RFC8250.
If you don't want to support that yet, that's fine.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going off of RF5280 4.2.1.13. where it says

Conforming CAs SHOULD NOT use nameRelativeToCRLIssuer to specify distribution point names.

But I might have misunderstood the scope of that statement.

Comment on lines +149 to +154
if cert.subject_public_key_info.algorithm != crl.signature_algorithm {
return Err(Error::SignatureTypeMismatch(
Box::new(cert.subject_public_key_info.algorithm.clone()),
Box::new(crl.signature_algorithm),
));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is wrong, the crl should be signed by the issuer public key, not the subject public key.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to respond to this and the comment below simultaneously. That comment copied below for posterity:

@tristanmiller-spruceid could you please address the outstanding issues?

The CRL is verified against the certificate subject's public key, instead of the certificate issuer. This only works if the certificate being checked is self-signed (e.g. the root). The current API does not support checking the CRL of an intermediate certificate, unless it happens that the intermediate certificate and the root certificate have the CRL.

To resolve this please consider implementing the public API as described in the issue:
#76 (comment)

First, given the complexity of certificate chains, I've split the public API of the crl crate into two halves, 1) given any cert in the chain, grab the CRLs that this cert signs and check that CRL's validity (fetch_and_validate_crl) and 2) given a CRL and a cert, return if the cert is revoked (check_cert_against_cert_lists). This allows for relatively arbitrary certificate chains as implemented by higher level code where there may be several layers of certificate chains.

So it's expected that calling code will iterate through their cert tree chain, attach all CRLs to that chain each grabbed with fetch_and_validate_crl, and validate each node in that chain against each of the child CRLs with check_cert_against_cert_lists.

Additionally, the CRLs are signed by the subject public key of the the cert who's distribution point specified them. That does not stop from checking the CRL of an intermediate certificate, one simply needs to call fetch_and_validate_crl against the intermediate certs as well. The argument to fetch_and_validate_crl has had it's name changed from root_cert to crl_signing_cert to make that more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, the CRLs are signed by the subject public key of the the cert who's distribution point specified them.

Correct me if I'm wrong, but I believe CRLs are signed by the issuer of the cert whose distribution point specified them, as you pointed out:

RFC 5280 6.3.1. CRL Processing (b)(1)

[V]erify that the CRL issuer matches the certificate issuer.

For a root CA, this happens to be itself as it is self-signed.

That does not stop from checking the CRL of an intermediate certificate, one simply needs to call fetch_and_validate_crl against the intermediate certs as well. The argument to fetch_and_validate_crl has had it's name changed from root_cert to crl_signing_cert to make that more clear.

The problem is that for an intermediate certificate the fetch_and_validate_crl function will fail because the intermediate certificate does not sign its own CRL, the CRL issuer is the certificate issuer.

src/definitions/x509/crl.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@cobward cobward left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tristanmiller-spruceid could you please address the outstanding issues?

The CRL is verified against the certificate subject's public key, instead of the certificate issuer. This only works if the certificate being checked is self-signed (e.g. the root). The current API does not support checking the CRL of an intermediate certificate, unless it happens that the intermediate certificate and the root certificate have the same CRL.

To resolve this please consider implementing the public API as described in the issue:
#76 (comment)

src/definitions/x509/crl.rs Outdated Show resolved Hide resolved
src/definitions/x509/crl.rs Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move this file? We normally keep test data in the ./test directory rather than in-line.

author Arjen van Veen <[email protected]> 1701187280 +0100
committer Arjen van Veen <[email protected]> 1717744754 +0200

refactor for readability WIP

clean up comments and duplicates

clean up, add some comments

cargo fmt

clippy fix

fmt

assert on tests

address pr comments

refactor handle_response to return a validated_response, submit parsing and decryption errors under errors

support creating a trust_anchor_registry from pem strings

Fix x5chain encoding.

X5Chain decoding fixes and version checking

Improve reader validation code.

- Also add a CLI tool for validating issuer certificates.

Fix public key parsing

Feat/reader auth cn (#79)

* rebase onto feat/mdoc-auth

* rebase and use mdoc-auth functions

* wip experiment with cert building

* small clean up

* Fix inconsistency. (#78)

* validated request improvements

---------

Co-authored-by: Jacob <[email protected]>

remove duplicate code

clippy fix
Base automatically changed from feat/mdoc-auth to main December 20, 2024 13:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants