1use std::collections::BTreeMap;
2use x509_cert::ext::pkix::CrlReason;
3
4use crate::error::Result;
5
6pub type Epoch = u64;
7pub type SerialBytes = Vec<u8>;
8
9#[derive(Debug, Clone)]
10pub enum CertificateStatus {
11 Good,
12 Revoked {
13 revocation_time: u64,
14 reason: Option<CrlReason>,
15 },
16}
17
18#[derive(Debug, Clone, Default)]
19pub struct StatusSnapshot {
20 pub entries: BTreeMap<SerialBytes, CertificateStatus>,
21 pub this_update: u64,
22 pub next_update: Option<u64>,
23 pub not_after: BTreeMap<SerialBytes, u64>,
28}
29
30#[derive(Debug, Clone)]
31pub struct StatusChange {
32 pub serial: SerialBytes,
33 pub status: CertificateStatus,
34 pub timestamp: u64,
35}
36
37#[derive(Debug, Clone)]
38pub struct CaIdentity {
39 pub label: String,
40 pub issuer_name_der: Vec<u8>,
41 pub issuer_key_bytes: Vec<u8>,
42}
43
44pub trait RevocationSource: Send + Sync {
45 fn snapshot(&self, ca: &CaIdentity) -> Result<StatusSnapshot>;
46 fn changes_since(&self, ca: &CaIdentity, since: Epoch) -> Result<Vec<StatusChange>>;
47 fn supports_streaming(&self) -> bool;
48 fn is_authoritative_complete(&self) -> bool {
50 false
51 }
52}
53
54impl StatusSnapshot {
55 pub fn validate_at(&self, now: u64) -> Result<u64> {
57 let end = self
58 .next_update
59 .ok_or_else(|| crate::error::SignError::Config("source has no nextUpdate".into()))?;
60 if self.this_update > now || end <= now || end <= self.this_update {
61 return Err(crate::error::SignError::Config(
62 "source validity is expired, future-dated or inconsistent".into(),
63 ));
64 }
65 Ok(end)
66 }
67}
68
69pub fn unix_now() -> Result<u64> {
70 std::time::SystemTime::now()
71 .duration_since(std::time::UNIX_EPOCH)
72 .map(|v| v.as_secs())
73 .map_err(|_| crate::error::SignError::Config("clock is before Unix epoch".into()))
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 #[test]
80 fn freshness_boundaries() {
81 let mut s = StatusSnapshot {
82 entries: BTreeMap::new(),
83 this_update: 10,
84 next_update: Some(20),
85 ..Default::default()
86 };
87 assert_eq!(s.validate_at(10).unwrap(), 20);
88 assert!(s.validate_at(9).is_err());
89 assert!(s.validate_at(20).is_err());
90 s.next_update = None;
91 assert!(s.validate_at(11).is_err());
92 }
93}