spiffe/bundle/x509/
mod.rs

1//! X.509 bundle types.
2
3use crate::bundle::{Bundle, BundleRefSource};
4use crate::cert::errors::CertificateError;
5use crate::cert::parsing::{parse_der_encoded_bytes_as_x509_certificate, to_certificate_vec};
6use crate::cert::Certificate;
7use crate::spiffe_id::TrustDomain;
8use std::collections::HashMap;
9use std::convert::TryFrom;
10use std::error::Error;
11
12/// This type contains a collection of trusted X.509 authorities for a [`TrustDomain`].
13#[derive(Debug, Clone, Eq, PartialEq)]
14pub struct X509Bundle {
15    trust_domain: TrustDomain,
16    x509_authorities: Vec<Certificate>,
17}
18
19impl Bundle for X509Bundle {}
20
21/// This type contains a set of [`X509Bundle`], keyed by [`TrustDomain`].
22#[derive(Debug, Clone, Eq, PartialEq)]
23pub struct X509BundleSet {
24    bundles: HashMap<TrustDomain, X509Bundle>,
25}
26
27/// An error that can arise trying to parse a [`X509Bundle`] from bytes
28/// representing `DER` encoded X.509 authorities.
29#[derive(Debug, thiserror::Error, PartialEq)]
30#[non_exhaustive]
31pub enum X509BundleError {
32    /// Error processing or validating the X.509 certificates in the bundle.
33    #[error(transparent)]
34    Certificate(#[from] CertificateError),
35}
36
37impl X509Bundle {
38    /// Creates an emtpy `X509Bundle` for the given [`TrustDomain`].
39    pub fn new(trust_domain: TrustDomain) -> Self {
40        X509Bundle {
41            trust_domain,
42            x509_authorities: Vec::new(),
43        }
44    }
45
46    /// Creates a bundle from a slice of X.509 authorities as ASN.1 DER-encoded data (binary format).
47    ///
48    /// # Arguments
49    ///
50    /// * `authorities` - ASN.1 DER-encoded data (binary format) representing a list X.509 authorities.
51    ///
52    /// # Error
53    ///
54    /// If the function cannot parse the inputs, a [`X509BundleError`] variant will be returned.
55    pub fn from_x509_authorities(
56        trust_domain: TrustDomain,
57        authorities: &[&[u8]],
58    ) -> Result<Self, X509BundleError> {
59        let mut x509_authorities = vec![];
60        for authority in authorities
61            .iter()
62            .map(|&bytes| Certificate::try_from(bytes))
63        {
64            x509_authorities.push(authority?);
65        }
66
67        Ok(X509Bundle {
68            trust_domain,
69            x509_authorities,
70        })
71    }
72
73    /// Parses a bundle from ASN.1 DER-encoded data (binary format) representing a list of X.509 authorities.
74    ///
75    /// # Arguments
76    ///
77    /// * `trust_domain` - A [`TrustDomain`] to associate to the bundle.
78    /// * `bundle_der` - ASN.1 DER-encoded data (binary format) representing a list of X.509 authorities.
79    ///
80    /// # Error
81    ///
82    /// If the function cannot parse the inputs, a [`X509BundleError`] variant will be returned.
83    pub fn parse_from_der(
84        trust_domain: TrustDomain,
85        bundle_der: &[u8],
86    ) -> Result<Self, X509BundleError> {
87        let x509_authorities = to_certificate_vec(bundle_der)?;
88
89        // validate that all authorities are valid X.509 certificates
90        for authority in x509_authorities.iter() {
91            parse_der_encoded_bytes_as_x509_certificate(authority.content())?;
92        }
93
94        Ok(X509Bundle {
95            trust_domain,
96            x509_authorities,
97        })
98    }
99
100    /// Adds an X.509 authority as ASN.1 DER-encoded data (binary format)to the bundle.
101    /// It verifies that the `authorities_bytes` represents a valid DER-encoded X.509 certificate.
102    ///
103    /// # Arguments
104    ///
105    /// * `authority_bytes` - ASN.1 DER-encoded data (binary format) representing a X.509 authority.
106    ///
107    /// # Error
108    ///
109    /// If the function cannot parse the inputs, a [`X509BundleError`] variant will be returned.
110    pub fn add_authority(&mut self, authority_bytes: &[u8]) -> Result<(), X509BundleError> {
111        let certificate = Certificate::try_from(authority_bytes)?;
112        self.x509_authorities.push(certificate);
113        Ok(())
114    }
115
116    /// Returns the [`TrustDomain`]associated to the bundle.
117    pub fn trust_domain(&self) -> &TrustDomain {
118        &self.trust_domain
119    }
120
121    /// Returns the X.509 authorities in the bundle.
122    pub fn authorities(&self) -> &Vec<Certificate> {
123        &self.x509_authorities
124    }
125}
126
127impl X509BundleSet {
128    /// Creates a new empty `X509BundleSet`.
129    pub fn new() -> Self {
130        X509BundleSet {
131            bundles: HashMap::new(),
132        }
133    }
134
135    /// Adds a new [`X509Bundle`] into the set. If a bundle already exists for the
136    /// trust domain, the existing bundle is replaced.
137    pub fn add_bundle(&mut self, bundle: X509Bundle) {
138        self.bundles.insert(bundle.trust_domain().clone(), bundle);
139    }
140
141    /// Returns the [`X509Bundle`] associated to the given [`TrustDomain`].
142    pub fn get_bundle(&self, trust_domain: &TrustDomain) -> Option<&X509Bundle> {
143        self.bundles.get(trust_domain)
144    }
145}
146
147impl Default for X509BundleSet {
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153impl BundleRefSource for X509BundleSet {
154    type Item = X509Bundle;
155
156    /// Returns the [`X509Bundle`] associated to the given [`TrustDomain`].
157    fn get_bundle_for_trust_domain(
158        &self,
159        trust_domain: &TrustDomain,
160    ) -> Result<Option<&Self::Item>, Box<dyn Error + Send + Sync + 'static>> {
161        Ok(self.bundles.get(trust_domain))
162    }
163}