briansmith / webpki

WebPKI X.509 Certificate Validation in Rust
https://briansmith.org/rustdoc/webpki/
Other
463 stars 165 forks source link

Allow user code to handle extensions that webpki does not understand #119

Closed Demi-Marie closed 4 years ago

Demi-Marie commented 4 years ago

Background

In libp2p, TLS certificates are not signed by a certificate authority (CA). Instead, self-signed certificates are used, and there is a custom critical extension that contains:

Furthermore, my understanding is that most other parts of the certificate are stubbed out. For example, libp2p does not require the Subject Alternative Name extension, or even the Basic Constraints extension. Since certificates used in libp2p are ephemeral, the issuer and subject may be zero-length stubs.

The exact procedure is part of the libp2p specification, which is the definitive document.

Currently, WebPKI fails to parse libp2p certificates, because it doesn’t understand the critical libp2p extension. WebPKI cannot validate such certificates itself, because doing so may require algorithms (such as ECDSA over secp256k1) that ring does not support. libp2p implementations handle most certificate validation themselves, and extract the node’s identity key from the certificate manually.

In order to be usable with libp2p, WebPKI needs to be able to parse libp2p X.509 certificates, and to be able to validate signatures made with their keys. However, webpki currently rejects such certificates at parse time.

Although libp2p is where I realized this problem, it is not the only place where it arises. Kerberos would have the same problem, as it too uses extensions that are not found on the web.

Suggested Solution

I propose the following API:

/// The return value of a custom extension validation callback.
///
/// Per the X.509 standard and [RFC5280], critical extensions that an
/// application does not know about cause the certificate to be rejected. This
/// behavior can be overriden by returning a value other than
/// [`ExtensionValidity::Unknown`].
#[must_use = "This might be a `Valid` or `Bogus` variant, which should be handled."]
enum ExtensionValidity {
    /// Process the extension as normal. If WebPKI does not know how to process
    /// it, it will:
    ///
    /// * reject the certificate if the extension is critical
    /// * ignore the extension if it is not critical.
    Unknown,
    /// Assert that the application knows about the extension, and has
    /// determined it to be valid. If the extension is known by WebPKI, this is
    /// equivalent to returning [`ExtensionValidity::Unknown`]. Otherwise,
    /// WebPKI will ignore the extension, even if it is critical.
    Valid,
    /// Tell WebPKI to unconditionally reject the certificate. The callback will
    /// not be called again for this certificate.
    Bogus,
}

/// A trait to be implemented by extension callbacks
trait ExtensionCallback {
    /// Called for each extension in the certificate.
    ///
    /// `oid` is the object identifier of the extension. `data` is the raw
    /// bytes of the extension value, as an [`&mut untrusted::Input`](untrusted::Input).
    fn on_extension(&mut self, oid: &[u64], data: untrusted::Input, critical: bool) -> ExtensionValidity;
}

impl EndEntityCert {
    /// Process extensions in this certificate. This allows applications and
    /// libraries to process certificates with critical extensions that WebPKI
    /// does not know about.
    ///
    /// By default, [`EndEntityCert::verify_signature`] does not reject
    /// certificates with unknown critical extensions, although it does check
    /// the `KeyUsage` extension. However,
    /// [`EndEntityCert::verify_is_valid_tls_server_cert`],
    /// [`EndEntityCert::verify_is_valid_tls_client_cert`],
    /// [`EndEntityCert::verify_is_valid_for_dns_name`], and
    /// [`EndEntityCert::verify_is_valid_for_at_least_one_dns_name`] do reject
    /// certificates with unknown critical extensions.
    fn process_extensions(
        &mut self,
        cb: &mut dyn ExtensionCallback,
    ) -> Result<(), ring::error::Unspecified> {
        // implementation
    }
}
Demi-Marie commented 4 years ago

@briansmith what are your thoughts?

briansmith commented 4 years ago

I'm not planning to add support for custom extension handlers. This project is about the Web PKI and helping to agree on and standardize improvements to the Web PKI. If there is an extension that we think warrants spending time on, we should add support for it directly to Web PKI.