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

Continued webpki CRL support. #66

Merged
merged 11 commits into from
Jun 15, 2023
55 changes: 42 additions & 13 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,54 @@ use crate::der::Tag;
use crate::x509::{remember_extension, set_extension_once, Extension};
use crate::{der, signed_data, Error};

/// An enumeration indicating whether a [`Cert`] is a leaf end-entity cert, or a linked
/// list node from the CA `Cert` to a child `Cert` it issued.
pub enum EndEntityOrCa<'a> {
/// The [`Cert`] is a leaf end-entity certificate.
EndEntity,

/// The [`Cert`] is an issuer certificate, and issued the referenced child `Cert`.
Ca(&'a Cert<'a>),
}

/// A parsed X509 certificate.
cpu marked this conversation as resolved.
Show resolved Hide resolved
pub struct Cert<'a> {
pub ee_or_ca: EndEntityOrCa<'a>,

pub serial: untrusted::Input<'a>,
pub signed_data: signed_data::SignedData<'a>,
pub issuer: untrusted::Input<'a>,
pub validity: untrusted::Input<'a>,
pub subject: untrusted::Input<'a>,
pub spki: der::Value<'a>,

pub basic_constraints: Option<untrusted::Input<'a>>,
pub eku: Option<untrusted::Input<'a>>,
pub name_constraints: Option<untrusted::Input<'a>>,
pub subject_alt_name: Option<untrusted::Input<'a>>,
pub(crate) ee_or_ca: EndEntityOrCa<'a>,

pub(crate) serial: untrusted::Input<'a>,
pub(crate) signed_data: signed_data::SignedData<'a>,
pub(crate) issuer: untrusted::Input<'a>,
pub(crate) validity: untrusted::Input<'a>,
pub(crate) subject: untrusted::Input<'a>,
pub(crate) spki: der::Value<'a>,

pub(crate) basic_constraints: Option<untrusted::Input<'a>>,
pub(crate) eku: Option<untrusted::Input<'a>>,
pub(crate) name_constraints: Option<untrusted::Input<'a>>,
pub(crate) subject_alt_name: Option<untrusted::Input<'a>>,
}

impl<'a> Cert<'a> {
/// Raw DER encoded certificate serial number.
pub fn serial(&self) -> &[u8] {
self.serial.as_slice_less_safe()
}

/// Raw DER encoded certificate issuer.
pub fn issuer(&self) -> &[u8] {
self.issuer.as_slice_less_safe()
}

/// Raw DER encoded certificate subject.
pub fn subject(&self) -> &[u8] {
self.subject.as_slice_less_safe()
}

/// Returns an indication of whether the certificate is an end-entity (leaf) certificate,
/// or a certificate authority.
pub fn end_entity_or_ca(&self) -> &EndEntityOrCa {
&self.ee_or_ca
}
}

pub(crate) fn parse_cert<'a>(
Expand Down
5 changes: 4 additions & 1 deletion src/crl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ impl<'a> TryFrom<&'a [u8]> for CertRevocationList<'a> {

// Iterate through the revoked certificate entries to ensure they are valid so we can
// yield an error up-front instead of on first iteration by the caller.
// TODO(@cpu): Should we do this up-front, or accept late discovery of CRL entry errs?
for cert_result in crl.into_iter() {
cert_result?;
}
Expand All @@ -160,6 +159,10 @@ impl<'a> CertRevocationList<'a> {
/// method will ignore any [`RevokedCert`] entries that do not parse successfully. To handle
/// parse errors use [`CertRevocationList`]'s [`IntoIterator`] trait.
pub fn find_serial(&self, serial: &[u8]) -> Option<RevokedCert> {
// TODO(XXX): This linear scan is sub-optimal from a performance perspective, but avoids
// any allocation. It would be nice to offer a speedier alternative for
// when the alloc feature is enabled:
// https://github.com/rustls/webpki/issues/80
self.into_iter()
.filter_map(|parse_res| parse_res.ok())
.find(|revoked_cert| revoked_cert.serial_number.eq(serial))
Expand Down
4 changes: 2 additions & 2 deletions src/der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ where
inner.read_all(error, decoder)
}

pub struct Value<'a> {
pub(crate) struct Value<'a> {
value: untrusted::Input<'a>,
}

impl<'a> Value<'a> {
pub fn value(&self) -> untrusted::Input<'a> {
pub(crate) fn value(&self) -> untrusted::Input<'a> {
self.value
}
}
Expand Down
36 changes: 21 additions & 15 deletions src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
#[cfg(feature = "alloc")]
use crate::subject_name::GeneralDnsNameRef;
use crate::{
cert, signed_data, subject_name, verify_cert, Error, SignatureAlgorithm, SubjectNameRef, Time,
TlsClientTrustAnchors, TlsServerTrustAnchors,
cert, signed_data, subject_name,
verify_cert::{self, RevocationCheckOptions},
Error, SignatureAlgorithm, SubjectNameRef, Time, TlsClientTrustAnchors, TlsServerTrustAnchors,
};

/// An end-entity certificate.
Expand Down Expand Up @@ -92,13 +93,15 @@ impl<'a> EndEntityCert<'a> {
time: Time,
) -> Result<(), Error> {
verify_cert::build_chain(
verify_cert::EKU_SERVER_AUTH,
supported_sig_algs,
trust_anchors,
intermediate_certs,
&verify_cert::ChainOptions {
required_eku_if_present: verify_cert::EKU_SERVER_AUTH,
supported_sig_algs,
trust_anchors,
intermediate_certs,
revocation: None,
},
&self.inner,
time,
0,
)
}

Expand All @@ -115,19 +118,22 @@ impl<'a> EndEntityCert<'a> {
/// time).
pub fn verify_is_valid_tls_client_cert(
&self,
supported_sig_algs: &[&SignatureAlgorithm],
&TlsClientTrustAnchors(trust_anchors): &TlsClientTrustAnchors,
intermediate_certs: &[&[u8]],
supported_sig_algs: &'a [&'a SignatureAlgorithm],
djc marked this conversation as resolved.
Show resolved Hide resolved
&TlsClientTrustAnchors(trust_anchors): &'a TlsClientTrustAnchors,
intermediate_certs: &'a [&'a [u8]],
time: Time,
revocation: Option<RevocationCheckOptions<'a>>,
) -> Result<(), Error> {
verify_cert::build_chain(
verify_cert::EKU_CLIENT_AUTH,
supported_sig_algs,
trust_anchors,
intermediate_certs,
&verify_cert::ChainOptions {
required_eku_if_present: verify_cert::EKU_CLIENT_AUTH,
supported_sig_algs,
trust_anchors,
intermediate_certs,
revocation,
},
&self.inner,
time,
0,
)
}

Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub enum Error {
/// for is earlier than the certificate's notBefore time.
CertNotValidYet,

/// The certificate, or one of its issuers, has been revoked.
CertRevoked,

/// An end-entity certificate is being used as a CA certificate.
EndEntityUsedAsCa,

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ mod verify_cert;
mod x509;

pub use {
cert::{Cert, EndEntityOrCa},
crl::{CertRevocationList, RevocationReason, RevokedCert},
end_entity::EndEntityCert,
error::Error,
Expand All @@ -67,6 +68,7 @@ pub use {
},
time::Time,
trust_anchor::{TlsClientTrustAnchors, TlsServerTrustAnchors, TrustAnchor},
verify_cert::{CrlProvider, RevocationCheckOptions},
};

// TODO(XXX): An interaction between Rust 1.57 and clippy requires working around 'unreachable_pub'
Expand Down
2 changes: 1 addition & 1 deletion src/signed_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use ring::signature;
/// X.509 certificates and related items that are signed are almost always
/// encoded in the format "tbs||signatureAlgorithm||signature". This structure
/// captures this pattern.
pub struct SignedData<'a> {
pub(crate) struct SignedData<'a> {
/// The signed data. This would be `tbsCertificate` in the case of an X.509
/// certificate, `tbsResponseData` in the case of an OCSP response, and the
/// data nested in the `digitally-signed` construct for TLS 1.2 signed
Expand Down
Loading