pkix_path_builder/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![forbid(unsafe_code)]
4#![warn(missing_docs, rust_2018_idioms)]
5
6//! RFC 4158 certification path building for [`pkix_path`].
7//!
8//! Accepts an unordered collection of certificates ([`CertPool`]) and
9//! constructs a valid ordered chain suitable for [`pkix_path::validate_path`].
10//!
11//! # Relationship to `pkix-path`
12//!
13//! `pkix-path` validates a caller-ordered `&[Certificate]`. This crate
14//! handles the prior step: discovering and ordering that chain from a bag
15//! of certificates when the caller does not know the chain order in advance.
16//! Cross-certificates and bridge CA topologies are handled here, not in
17//! `pkix-path`.
18//!
19//! # Spec references
20//!
21//! - RFC 4158 — Internet X.509 PKI: Certification Path Building
22//! - RFC 5280 §6.1 — the validation algorithm this crate feeds into
23//!
24//! # Limitations
25//!
26//! Not yet implemented. See PKIX-y2j.
27
28extern crate alloc;
29
30use alloc::vec::Vec;
31use x509_cert::Certificate;
32
33/// An unordered collection of certificates used as input to path building.
34///
35/// Certificates are stored by DER bytes and decoded on demand. Add all
36/// candidate intermediate certificates here; the path builder will select
37/// and order the subset that forms a valid path to a trust anchor.
38#[derive(Debug, Default)]
39pub struct CertPool {
40 certs: Vec<Certificate>,
41}
42
43impl CertPool {
44 /// Create an empty pool.
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 /// Add a certificate to the pool.
50 pub fn add(&mut self, cert: Certificate) {
51 self.certs.push(cert);
52 }
53
54 /// Return the number of certificates in the pool.
55 pub fn len(&self) -> usize {
56 self.certs.len()
57 }
58
59 /// Return `true` if the pool contains no certificates.
60 pub fn is_empty(&self) -> bool {
61 self.certs.is_empty()
62 }
63}
64
65/// Errors returned by path building.
66#[derive(Debug)]
67#[non_exhaustive]
68pub enum Error {
69 /// No valid path from the target certificate to any trust anchor was found.
70 NoPathFound,
71 /// Path building exceeded the configured maximum candidate depth.
72 DepthExceeded,
73}
74
75impl core::fmt::Display for Error {
76 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77 match self {
78 Error::NoPathFound => f.write_str("no certification path found to a trust anchor"),
79 Error::DepthExceeded => f.write_str("path building exceeded maximum candidate depth"),
80 }
81 }
82}
83
84#[cfg(feature = "std")]
85impl std::error::Error for Error {}
86
87/// Result alias for this crate.
88pub type Result<T> = core::result::Result<T, Error>;
89
90/// Build a certification path from `target` through certificates in `pool`
91/// to one of the provided trust anchors.
92///
93/// Returns the ordered chain `[target, intermediate..., issuer]` ready for
94/// [`pkix_path::validate_path`].
95///
96/// # Errors
97///
98/// Returns [`Error::NoPathFound`] if no valid path exists in `pool`.
99///
100/// # Limitations
101///
102/// Not yet implemented (PKIX-y2j).
103pub fn build_path(
104 _target: &Certificate,
105 _pool: &CertPool,
106 _anchors: &[pkix_path::TrustAnchor],
107) -> Result<Vec<Certificate>> {
108 Err(Error::NoPathFound)
109}