//! Trust material storage: a simple v1 CRL (set of revoked identifiers). //! //! The Aura v1 revocation list is deliberately minimal: a set of opaque //! identifier strings. An identifier is either a certificate serial number //! (lowercase hex, no separators) or a client id / Common Name. A certificate //! is rejected if any of those identifiers is present in the set. use std::collections::BTreeSet; use std::fs; use std::path::Path; use anyhow::Context; /// A set of revoked certificate identifiers (serials and/or client ids). #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct CrlStore { revoked: BTreeSet, } impl CrlStore { /// Create an empty CRL. pub fn new() -> Self { Self::default() } /// Add a single revoked identifier (serial hex or client id). pub fn revoke(&mut self, id: impl Into) { self.revoked.insert(id.into()); } /// True if `id` is in the revocation set. pub fn contains(&self, id: &str) -> bool { self.revoked.contains(id) } /// True if no certificates are revoked. pub fn is_empty(&self) -> bool { self.revoked.is_empty() } /// Number of revoked identifiers. pub fn len(&self) -> usize { self.revoked.len() } /// Iterate over the revoked identifiers (sorted). pub fn iter(&self) -> impl Iterator { self.revoked.iter().map(String::as_str) } /// Persist the CRL, one identifier per line. pub fn save(&self, path: &Path) -> anyhow::Result<()> { let mut body = String::new(); for id in &self.revoked { body.push_str(id); body.push('\n'); } fs::write(path, body).with_context(|| format!("writing CRL to {}", path.display()))?; Ok(()) } /// Load a CRL written by [`CrlStore::save`] (one identifier per line; blank /// lines and `#` comments are ignored). pub fn load(path: &Path) -> anyhow::Result { let text = fs::read_to_string(path) .with_context(|| format!("reading CRL from {}", path.display()))?; Ok(Self::from_iter( text.lines() .map(str::trim) .filter(|l| !l.is_empty() && !l.starts_with('#')) .map(str::to_string), )) } } impl FromIterator for CrlStore { fn from_iter>(iter: T) -> Self { Self { revoked: iter.into_iter().collect(), } } } impl Extend for CrlStore { fn extend>(&mut self, iter: T) { self.revoked.extend(iter); } }