Skip to main content

x509_validator/
validated_chain.rs

1use crate::Certificate;
2
3/// A certificate chain that has passed policy evaluation, leaf-first.
4#[derive(Debug, Clone)]
5pub struct ValidatedCertificateChain<'a> {
6    certificates: Vec<Certificate<'a>>, // leaf-first
7}
8
9impl<'a> ValidatedCertificateChain<'a> {
10    pub fn new_unchecked(certificates: Vec<Certificate<'a>>) -> Self {
11        assert!(!certificates.is_empty());
12        Self { certificates }
13    }
14
15    pub fn leaf(&self) -> &Certificate<'a> {
16        &self.certificates[0]
17    }
18    pub fn root(&self) -> &Certificate<'a> {
19        self.certificates.last().unwrap()
20    }
21
22    pub fn iter(&self) -> impl Iterator<Item = &Certificate<'a>> {
23        self.certificates.iter()
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use x509_validator_testkit::{cert, issue_ca, issue_leaf, self_signed_ca_with};
30
31    use super::*;
32    use crate::CertificateExt;
33
34    /// A three-certificate chain, leaf-first: leaf, intermediate, root.
35    fn leaf_intermediate_root() -> Vec<Certificate<'static>> {
36        let root = self_signed_ca_with("Root", |_| {});
37        let intermediate = issue_ca("Intermediate", &root, None, |_| {});
38        let leaf = issue_leaf("leaf.example.com", &["leaf.example.com"], &intermediate);
39
40        vec![cert(leaf), cert(intermediate.der), cert(root.der)]
41    }
42
43    #[test]
44    fn leaf_and_root_are_the_two_ends_of_the_chain() {
45        let chain = ValidatedCertificateChain::new_unchecked(leaf_intermediate_root());
46
47        assert_eq!(chain.leaf().subject().to_string(), "CN=leaf.example.com");
48        assert_eq!(chain.root().subject().to_string(), "CN=Root");
49    }
50
51    #[test]
52    fn root_is_the_self_signed_end_not_the_leaf() {
53        let chain = ValidatedCertificateChain::new_unchecked(leaf_intermediate_root());
54
55        let root = chain.root();
56        assert_eq!(root.subject_key(), root.issuer_key());
57        assert_ne!(chain.leaf().subject_key(), chain.leaf().issuer_key());
58    }
59
60    #[test]
61    fn iter_yields_the_chain_leaf_first() {
62        let chain = ValidatedCertificateChain::new_unchecked(leaf_intermediate_root());
63
64        let subjects: Vec<_> = chain
65            .iter()
66            .map(|c| c.subject().to_string())
67            .collect();
68
69        assert_eq!(
70            subjects,
71            ["CN=leaf.example.com", "CN=Intermediate", "CN=Root"]
72        );
73    }
74
75    #[test]
76    fn a_single_certificate_is_both_leaf_and_root() {
77        let root = cert(self_signed_ca_with("Root", |_| {}).der);
78        let chain = ValidatedCertificateChain::new_unchecked(vec![root]);
79
80        assert_eq!(chain.leaf().subject().to_string(), "CN=Root");
81        assert_eq!(chain.root().subject().to_string(), "CN=Root");
82        assert!(
83            chain
84                .leaf()
85                .has_same_identity_as(chain.root())
86        );
87    }
88
89    #[test]
90    fn new_unchecked_performs_no_validation() {
91        // Two unrelated self-signed certificates: nothing issues anything
92        // else. The constructor is named `_unchecked` because it accepts
93        // this — policy evaluation is the caller's job.
94        let a = cert(self_signed_ca_with("A", |_| {}).der);
95        let b = cert(self_signed_ca_with("B", |_| {}).der);
96
97        let chain = ValidatedCertificateChain::new_unchecked(vec![a, b]);
98
99        assert_ne!(chain.leaf().issuer_key(), chain.root().subject_key());
100    }
101
102    #[test]
103    #[should_panic]
104    fn constructing_an_empty_chain_panics() {
105        ValidatedCertificateChain::new_unchecked(Vec::new());
106    }
107}