X509 Path Finder is a depth-first search certificate path validator for Rust.
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:
- Number of certificates preloaded into its local store
- Number of certificates it can find and download by following AIA URLs
- 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:
- DefaultPathValidator - implemented with rustls-webpki, available by default.
- OpenSSLPathValidator - implemented with Rust OpenSSL, available with the
openssl
feature flag
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.
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"] }
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(), &[], true);
// instantiate the finder
let mut search = X509PathFinder::new(X509PathFinderConfiguration {
limit: Duration::default(),
#[cfg(feature = "resolve")]
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(())
}
The X509 Path Builder is configured with the X509PathFinderConfiguration
struct, which has the following fields:
limit
: limit execution time of path search. Actual limit will be N * HTTP timeout. Seereqwest::ClientBuilder::timeout
for setting HTTP connection timeout.aia
: optionalx509_client::X509ClientConfiguration
to enable Authority Information Access extensions.validator
:PathValidator
implementationcertificates
: additional intermediate, bridge and cross signed-certificates to use for path finding. These certificates are considered for path candidacy first, and are tried in order.
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:
- Set the
limit
duration to non-zero forX509PathFinderConfiguration::limit
- Set the
reqwest::ClientBuilder::timeout
to a more aggressive value - Limit the certificate download size by setting
x509_client::X509ClientConfiguration::limit
to a non-zero value - Disable AIA
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, containsFound
duration
: duration of path searchstore
: collection of cachedCertificate
not used in a discovered pathfailures
: any validation failures reported byPathValidator
implementations are held inValidationFailure
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
is an enum that describes the origin of each certificate. Can be one of:
Target
: the initial certificate when callingX509PathFinder::find
.Store
: certificate was found in the storeUrl
: certificate was downloaded from a URL (AIA)
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 ofCertificate
where the validation error occurredorigin
: theCertificateOrigin
of where the validation error occurredreason
: human-readable reason for the failure
ValidationFailure
is also an iterator over references of members of path
.
The X509 PathValidator
API can be implemented to use different backend authorities to validate certificate paths and add business logic, especially policy constraints.
- DefaultPathValidator- validates path with rustls-webpki
- OpenSSLPathValidator- validates path with OpenSSL
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