#path #x509 #path-finding #search #validate #validation #aia

x509-path-finder

X509 Path Finder is a depth-first search certificate path validator for Rust

9 releases (4 breaking)

0.7.1 Sep 20, 2023
0.6.3 Sep 7, 2023
0.5.2 Sep 5, 2023
0.3.5 Aug 31, 2023
0.2.1 Aug 24, 2023

#271 in Network programming

30 downloads per month

Apache-2.0

215KB
1.5K SLoC

X509 Path Finder

X509 Path Finder is a depth-first search certificate path validator for Rust.

CI Status

Depth-first search

Synopsis

X509 Path Finder is inspired by RFC 4158:

Many PKIs are now using complex structures... rather than simple hierarchies. Additionally, some enterprises are gradually moving away from trust lists filled with many trust anchors, and toward an infrastructure with one trust anchor and many cross-certified relationships.... This document suggests using an effective general approach to path building that involves a depth first tree traversal.

X509 Path Finder rejects the notion of a single certificate "chain." Instead, it searches for the first match out of infinity. Once it finds a path it can validate, the search halts and the path is returned to the caller.

The complexity of the path search is constrained by three factors:

  1. Number of certificates preloaded into its local store
  2. Number of certificates it can find and download by following AIA URLs
  3. An arbitrary time limit

When evaluating a path candidate for validation, X509 Path Finder is implementation-agnostic. Once it finds a path that has terminated, it presents it to be validated by a backend authority. If the authority validates the path, the search halts and the path is returned. If the path is rejected, the search continues.

X509 Path Finder provides two PathValidator implementations:

  1. DefaultPathValidator - implemented with rustls-webpki, available by default.
  2. OpenSSLPathValidator - implemented with Rust OpenSSL, available with the openssl feature flag

WARNING

Implementing PathValidator itself is trivial, but safe X509 path validation is not. Engineers are encouraged to use a trusted X509 path validator for the foundation of their PathValidator implementations, and stack business logic on top.

Usage

By default, the provided DefaultPathValidator validator is available.

[dependencies]
x509_path_finder = { version = "*"] }

Enable the openssl feature for access to the provided OpenSSLPathValidator validator.

[dependencies]
x509_path_finder = { version = "*", features = ["openssl"] }

Example


    use webpki::{KeyUsage, TrustAnchor};
    use std::sync::Arc;
    use std::time::Duration;
    use x509_path_finder::provided::validator::default::DefaultPathValidator;
    use x509_path_finder::{X509PathFinder, X509PathFinderConfiguration};

    async fn test_find(
        root: Vec<u8>,
        ic: Vec<Arc<x509_path_finder::Certificate>>,
        ee: x509_path_finder::Certificate,
    ) -> Result<(), x509_path_finder::X509PathFinderError> {
        // instantiate default validator        
        let root = TrustAnchor::try_from_cert_der(root.as_slice()).unwrap();  
        let algorithms = &[&webpki::ECDSA_P256_SHA256];
        let validator = DefaultPathValidator::new(algorithms, vec![root], KeyUsage::client_auth(), &[]);

        // instantiate the finder
        let mut search = X509PathFinder::new(X509PathFinderConfiguration {
            limit: Duration::default(),
            aia: None,
            validator,
            certificates: ic,
        });

        // execute the search
        let found = search.find(ee).await?.found.unwrap();

        // path has two certificates
        assert_eq!(2, found.path.len());

        // Found is also an iterator over path
        assert_eq!(2, found.into_iter().count());
        
        Ok(())
    }

Configuration

The X509 Path Builder is configured with the X509PathFinderConfiguration struct, which has the following fields:

Resource Management

Because X509 Path Builder can consume AIA URLs from the web, a call to X509PathFinder::find could in theory run forever, or be coerced into downloading large amounts of data. Resource consumption can be managed with the following configuration settings:

Finding Paths

Call X509PathFinder::find to find a path. Supply the target end-entity Certificate to start from. The search will work backward toward the root certificate.

The returning Report contains the following fields:

  • found: on path find success, contains Found
  • duration: duration of path search
  • store: collection of cached Certificate not used in a discovered path
  • failures: any validation failures reported by PathValidator implementations are held in ValidationFailure

Found

The Found struct contains following fields:

  • path - the discovered path, a vec of Certificate The path includes the target certificate. Per RFC 5246, the path is ordered starting with the target, toward the trust anchor.
  • origin - the path CertificateOrigin

Found is also an iterator over references of members of path.

CertificateOrigin

CertificateOrigin is an enum that describes the origin of each certificate. Can be one of:

  • Target: the initial certificate when calling X509PathFinder::find.
  • Store: certificate was found in the store
  • Url: certificate was downloaded from a URL (AIA)

ValidateFailure

ValidationFailure stores any validation failures. Validation failures can occur even though a valid certificate path was eventually discovered. ValidateFailure contains the following fields:

  • path - Vec path of Certificate where the validation error occurred
  • origin: the CertificateOrigin of where the validation error occurred
  • reason: human-readable reason for the failure

ValidationFailure is also an iterator over references of members of path.

API

The X509 PathValidator API can be implemented to use different backend authorities to validate certificate paths and add business logic, especially policy constraints.

Implementations

TODO

Ordered by priority:

  • Deeper integration tests
  • Benchmarking
  • Cache issuer <-> subject mapping while building path
  • Ignore invalid certificates on ingest, rather than wait for PathValidator to reject the entire path candidate
  • Parallelize AIA downloads
  • Weighted path decisions

Dependencies

~13–29MB
~528K SLoC