spiffe_rs/bundle/x509bundle/
mod.rs

1use crate::internal::pemutil;
2use crate::internal::x509util;
3use crate::spiffeid::TrustDomain;
4use std::collections::HashMap;
5use std::fs;
6use std::io::Read;
7use std::sync::RwLock;
8
9#[derive(Debug, Clone)]
10pub struct Error(String);
11
12impl std::fmt::Display for Error {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        self.0.fmt(f)
15    }
16}
17
18impl std::error::Error for Error {}
19
20impl Error {
21    pub fn new(message: impl Into<String>) -> Error {
22        Error(message.into())
23    }
24}
25
26pub type Result<T> = std::result::Result<T, Error>;
27
28fn wrap_error(message: impl std::fmt::Display) -> Error {
29    Error(format!("x509bundle: {}", message))
30}
31
32/// An X.509 bundle contains the X.509 authorities for a trust domain.
33#[derive(Debug)]
34pub struct Bundle {
35    trust_domain: TrustDomain,
36    x509_authorities: RwLock<Vec<Vec<u8>>>,
37}
38
39impl Bundle {
40    /// Creates a new empty `Bundle` for the given trust domain.
41    pub fn new(trust_domain: TrustDomain) -> Bundle {
42        Bundle {
43            trust_domain,
44            x509_authorities: RwLock::new(Vec::new()),
45        }
46    }
47
48    /// Creates a new `Bundle` for the given trust domain and authorities.
49    pub fn from_x509_authorities(trust_domain: TrustDomain, authorities: &[Vec<u8>]) -> Bundle {
50        Bundle {
51            trust_domain,
52            x509_authorities: RwLock::new(x509util::copy_x509_authorities(authorities)),
53        }
54    }
55
56    /// Loads an X.509 bundle from a PEM encoded file.
57    pub fn load(trust_domain: TrustDomain, path: &str) -> Result<Bundle> {
58        let bytes = fs::read(path)
59            .map_err(|err| wrap_error(format!("unable to load X.509 bundle file: {}", err)))?;
60        Bundle::parse(trust_domain, &bytes)
61    }
62
63    /// Reads an X.509 bundle from a reader.
64    pub fn read(trust_domain: TrustDomain, reader: &mut dyn Read) -> Result<Bundle> {
65        let mut bytes = Vec::new();
66        reader
67            .read_to_end(&mut bytes)
68            .map_err(|err| wrap_error(format!("unable to read X.509 bundle: {}", err)))?;
69        Bundle::parse(trust_domain, &bytes)
70    }
71
72    /// Parses an X.509 bundle from PEM encoded bytes.
73    pub fn parse(trust_domain: TrustDomain, bytes: &[u8]) -> Result<Bundle> {
74        let bundle = Bundle::new(trust_domain);
75        if bytes.is_empty() {
76            return Ok(bundle);
77        }
78        let certs = pemutil::parse_certificates(bytes)
79            .map_err(|err| wrap_error(format!("cannot parse certificate: {}", err)))?;
80        for cert in certs {
81            bundle.add_x509_authority(&cert);
82        }
83        Ok(bundle)
84    }
85
86    /// Parses an X.509 bundle from DER encoded bytes.
87    pub fn parse_raw(trust_domain: TrustDomain, bytes: &[u8]) -> Result<Bundle> {
88        let bundle = Bundle::new(trust_domain);
89        if bytes.is_empty() {
90            return Ok(bundle);
91        }
92        let certs = parse_raw_certificates(bytes)
93            .map_err(|err| wrap_error(format!("cannot parse certificate: {}", err)))?;
94        for cert in certs {
95            bundle.add_x509_authority(&cert);
96        }
97        Ok(bundle)
98    }
99
100    /// Returns the trust domain of the bundle.
101    pub fn trust_domain(&self) -> TrustDomain {
102        self.trust_domain.clone()
103    }
104
105    /// Returns the X.509 authorities in the bundle, DER encoded.
106    pub fn x509_authorities(&self) -> Vec<Vec<u8>> {
107        self.x509_authorities
108            .read()
109            .map(|guard| x509util::copy_x509_authorities(&guard))
110            .unwrap_or_default()
111    }
112
113    /// Adds an X.509 authority to the bundle.
114    pub fn add_x509_authority(&self, authority: &[u8]) {
115        if let Ok(mut guard) = self.x509_authorities.write() {
116            if guard.iter().any(|cert| cert == authority) {
117                return;
118            }
119            guard.push(authority.to_vec());
120        }
121    }
122
123    /// Removes an X.509 authority from the bundle.
124    pub fn remove_x509_authority(&self, authority: &[u8]) {
125        if let Ok(mut guard) = self.x509_authorities.write() {
126            if let Some(index) = guard.iter().position(|cert| cert == authority) {
127                guard.remove(index);
128            }
129        }
130    }
131
132    /// Returns `true` if the bundle has the given X.509 authority.
133    pub fn has_x509_authority(&self, authority: &[u8]) -> bool {
134        self.x509_authorities
135            .read()
136            .map(|guard| guard.iter().any(|cert| cert == authority))
137            .unwrap_or(false)
138    }
139
140    /// Sets the X.509 authorities in the bundle.
141    pub fn set_x509_authorities(&self, authorities: &[Vec<u8>]) {
142        if let Ok(mut guard) = self.x509_authorities.write() {
143            *guard = x509util::copy_x509_authorities(authorities);
144        }
145    }
146
147    /// Returns `true` if the bundle is empty.
148    pub fn empty(&self) -> bool {
149        self.x509_authorities
150            .read()
151            .map(|guard| guard.is_empty())
152            .unwrap_or(true)
153    }
154
155    /// Marshals the bundle to PEM encoded bytes.
156    pub fn marshal(&self) -> Result<Vec<u8>> {
157        let certs = self.x509_authorities();
158        Ok(pemutil::encode_certificates(&certs))
159    }
160
161    /// Returns `true` if this bundle is equal to another bundle.
162    pub fn equal(&self, other: &Bundle) -> bool {
163        self.trust_domain == other.trust_domain
164            && x509util::certs_equal(&self.x509_authorities(), &other.x509_authorities())
165    }
166
167    /// Clones the bundle.
168    pub fn clone_bundle(&self) -> Bundle {
169        Bundle::from_x509_authorities(self.trust_domain(), &self.x509_authorities())
170    }
171
172    /// Returns the bundle for the given trust domain if it matches.
173    pub fn get_x509_bundle_for_trust_domain(&self, trust_domain: TrustDomain) -> Result<Bundle> {
174        if self.trust_domain != trust_domain {
175            return Err(wrap_error(format!(
176                "no X.509 bundle found for trust domain: \"{}\"",
177                trust_domain
178            )));
179        }
180        Ok(self.clone_bundle())
181    }
182}
183
184/// A source of X.509 bundles.
185pub trait Source {
186    /// Returns the X.509 bundle for the given trust domain.
187    fn get_x509_bundle_for_trust_domain(&self, trust_domain: TrustDomain) -> Result<Bundle>;
188}
189
190/// A set of X.509 bundles for multiple trust domains.
191#[derive(Debug)]
192pub struct Set {
193    bundles: RwLock<HashMap<TrustDomain, Bundle>>,
194}
195
196impl Set {
197    /// Creates a new `Set` from the given bundles.
198    pub fn new(bundles: &[Bundle]) -> Set {
199        let mut map = HashMap::new();
200        for bundle in bundles {
201            map.insert(bundle.trust_domain(), bundle.clone_bundle());
202        }
203        Set {
204            bundles: RwLock::new(map),
205        }
206    }
207
208    /// Adds a bundle to the set.
209    pub fn add(&self, bundle: &Bundle) {
210        if let Ok(mut guard) = self.bundles.write() {
211            guard.insert(bundle.trust_domain(), bundle.clone_bundle());
212        }
213    }
214
215    /// Removes the bundle for the given trust domain from the set.
216    pub fn remove(&self, trust_domain: TrustDomain) {
217        if let Ok(mut guard) = self.bundles.write() {
218            guard.remove(&trust_domain);
219        }
220    }
221
222    /// Returns `true` if the set has a bundle for the given trust domain.
223    pub fn has(&self, trust_domain: TrustDomain) -> bool {
224        self.bundles
225            .read()
226            .map(|guard| guard.contains_key(&trust_domain))
227            .unwrap_or(false)
228    }
229
230    /// Returns the bundle for the given trust domain from the set.
231    pub fn get(&self, trust_domain: TrustDomain) -> Option<Bundle> {
232        self.bundles
233            .read()
234            .ok()
235            .and_then(|guard| guard.get(&trust_domain).map(|b| b.clone_bundle()))
236    }
237
238    /// Returns all bundles in the set.
239    pub fn bundles(&self) -> Vec<Bundle> {
240        let mut bundles = self
241            .bundles
242            .read()
243            .map(|guard| guard.values().map(|b| b.clone_bundle()).collect::<Vec<_>>())
244            .unwrap_or_default();
245        bundles.sort_by(|a, b| a.trust_domain().compare(&b.trust_domain()));
246        bundles
247    }
248
249    /// Returns the number of bundles in the set.
250    pub fn len(&self) -> usize {
251        self.bundles.read().map(|guard| guard.len()).unwrap_or(0)
252    }
253
254    /// Returns the X.509 bundle for the given trust domain.
255    pub fn get_x509_bundle_for_trust_domain(&self, trust_domain: TrustDomain) -> Result<Bundle> {
256        let guard = self
257            .bundles
258            .read()
259            .map_err(|_| wrap_error("bundle store poisoned"))?;
260        let bundle = guard.get(&trust_domain).ok_or_else(|| {
261            wrap_error(format!(
262                "no X.509 bundle for trust domain \"{}\"",
263                trust_domain
264            ))
265        })?;
266        Ok(bundle.clone_bundle())
267    }
268}
269
270impl Source for Set {
271    fn get_x509_bundle_for_trust_domain(&self, trust_domain: TrustDomain) -> Result<Bundle> {
272        self.get_x509_bundle_for_trust_domain(trust_domain)
273    }
274}
275
276impl Source for Bundle {
277    fn get_x509_bundle_for_trust_domain(&self, trust_domain: TrustDomain) -> Result<Bundle> {
278        self.get_x509_bundle_for_trust_domain(trust_domain)
279    }
280}
281
282fn parse_raw_certificates(bytes: &[u8]) -> std::result::Result<Vec<Vec<u8>>, String> {
283    let mut remaining = bytes;
284    let mut certs = Vec::new();
285    while !remaining.is_empty() {
286        let (rest, _cert) = x509_parser::parse_x509_certificate(remaining)
287            .map_err(|err| err.to_string())?;
288        let consumed = remaining
289            .len()
290            .checked_sub(rest.len())
291            .ok_or_else(|| "invalid certificate length".to_string())?;
292        certs.push(remaining[..consumed].to_vec());
293        remaining = rest;
294    }
295    Ok(certs)
296}