use alloc::vec::Vec;
use crate::tls::Error;
use crate::x509::CertificateRevocationList;
#[derive(Clone, Default)]
pub struct CrlStore {
crls: Vec<CertificateRevocationList>,
}
impl CrlStore {
pub fn new() -> Self {
CrlStore { crls: Vec::new() }
}
pub fn add_der(&mut self, der: Vec<u8>) -> Result<(), Error> {
let crl = CertificateRevocationList::from_der(der).map_err(|_| Error::BadCertificate)?;
crl.check_signature_algid_consistent()
.map_err(|_| Error::BadCertificate)?;
crl.issuer().map_err(|_| Error::BadCertificate)?;
crl.entries().map_err(|_| Error::BadCertificate)?;
self.crls.push(crl);
Ok(())
}
pub fn add_pem(&mut self, pem: &str) -> Result<(), Error> {
let crl = CertificateRevocationList::from_pem(pem).map_err(|_| Error::BadCertificate)?;
self.add_der(crl.to_der().to_vec())
}
pub fn len(&self) -> usize {
self.crls.len()
}
pub fn is_empty(&self) -> bool {
self.crls.is_empty()
}
pub fn clone_store(&self) -> Self {
self.clone()
}
pub(crate) fn crls_with_issuer<'a>(
&'a self,
name_der: &'a [u8],
) -> impl Iterator<Item = &'a CertificateRevocationList> + 'a {
self.crls
.iter()
.filter(move |crl| crl.issuer_der().map(|d| d == name_der).unwrap_or(false))
}
pub(crate) fn merged_with(&self, other: &CrlStore) -> CrlStore {
let mut out = CrlStore {
crls: Vec::with_capacity(self.crls.len() + other.crls.len()),
};
out.crls.extend(self.crls.iter().cloned());
out.crls.extend(other.crls.iter().cloned());
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rsa::BoxedRsaPrivateKey;
use crate::x509::{CertSigner, CrlBuilder, DistinguishedName, Time};
fn rsa_a() -> BoxedRsaPrivateKey {
BoxedRsaPrivateKey::from_pkcs1_pem(include_str!("../../../testdata/rsa2048_test_a.pem"))
.expect("rsa key A")
}
#[test]
fn add_der_and_lookup_by_issuer() {
let key = rsa_a();
let dn = DistinguishedName::common_name("crl-store-test");
let mut b = CrlBuilder::new(&dn, Time::utc(2026, 1, 1, 0, 0, 0), None);
b.revoke(&[0x42], Time::utc(2026, 1, 2, 0, 0, 0), None);
let crl = b.sign(&CertSigner::Rsa(&key)).unwrap();
let mut store = CrlStore::new();
assert!(store.is_empty());
store.add_der(crl.to_der().to_vec()).unwrap();
assert_eq!(store.len(), 1);
let dn_der = dn.to_der();
let found: Vec<_> = store.crls_with_issuer(&dn_der).collect();
assert_eq!(found.len(), 1);
assert!(
found[0]
.verify_signature_with(&CertSigner::Rsa(&key).public_key())
.is_ok()
);
let other = DistinguishedName::common_name("nope").to_der();
assert_eq!(store.crls_with_issuer(&other).count(), 0);
}
#[test]
fn add_der_rejects_garbage() {
let mut store = CrlStore::new();
assert!(store.add_der(alloc::vec![0u8; 4]).is_err());
let bogus = alloc::vec![0x30, 0x00];
assert!(store.add_der(bogus).is_err());
}
}