Use S3 object expiry for OCSP snapshots

This commit is contained in:
2026-10-28 19:39:51 +00:00
parent 9da68a9625
commit a7cfda4167
17 changed files with 321 additions and 238 deletions
+3 -32
View File
@@ -8,7 +8,7 @@ use sha1::{Digest as _, Sha1};
use sha2::Sha256;
use x509_cert::ext::pkix::CrlReason;
pub const SCHEMA: &str = "kca-ocsp-status/v1";
pub const SCHEMA: &str = "kca-ocsp-status/v2";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
@@ -25,7 +25,6 @@ pub struct StatusEntry {
#[derive(Debug, Clone)]
pub struct Snapshot {
pub generated_at: DateTime<Utc>,
pub entries: HashMap<Vec<u8>, StatusEntry>,
}
@@ -33,7 +32,6 @@ pub struct Snapshot {
struct RawSnapshot {
schema: String,
issuer: RawIssuer,
generated_at: DateTime<Utc>,
#[serde(default)]
entries: Vec<RawEntry>,
}
@@ -67,9 +65,6 @@ impl Snapshot {
if raw.schema != SCHEMA {
return Err(SnapshotError::Schema(raw.schema));
}
if raw.generated_at > Utc::now() {
return Err(SnapshotError::FutureGeneration(raw.generated_at));
}
if !raw.issuer.id.eq_ignore_ascii_case(expected_ca_id) {
return Err(SnapshotError::IssuerIdMismatch {
expected: expected_ca_id.to_string(),
@@ -115,10 +110,7 @@ impl Snapshot {
}
}
let _ = raw.issuer.sha256;
Ok(Self {
generated_at: raw.generated_at,
entries,
})
Ok(Self { entries })
}
pub fn status_for_serial(&self, serial: &[u8]) -> Option<&StatusEntry> {
@@ -178,8 +170,6 @@ pub enum SnapshotError {
Json(#[from] serde_json::Error),
#[error("unsupported snapshot schema {0:?}")]
Schema(String),
#[error("snapshot generation time {0} is in the future")]
FutureGeneration(DateTime<Utc>),
#[error("snapshot issuer ID {actual} does not match configured CA ID {expected}")]
IssuerIdMismatch { expected: String, actual: String },
#[error("snapshot issuer {actual} does not match configured CA {expected}")]
@@ -201,9 +191,8 @@ mod tests {
fn body(sha: &str) -> String {
format!(
r#"{{
"schema": "kca-ocsp-status/v1",
"schema": "kca-ocsp-status/v2",
"issuer": {{"id": "ca", "sha256": "{sha}", "subject": "CN=CA"}},
"generated_at": "2026-01-01T00:00:00Z",
"entries": [
{{"serial_hex": "01ab", "status": "valid", "not_after": "2027-01-01T00:00:00Z"}},
{{"serial_hex": "00ff", "status": "revoked", "revoked_at": "2026-02-03T04:05:06Z", "reason": "keyCompromise"}}
@@ -238,24 +227,6 @@ mod tests {
assert!(Snapshot::parse(duplicate.as_bytes(), &sha, "ca").is_err());
}
#[test]
fn rejects_missing_or_future_generation_time() {
let sha = sha256_hex(b"ca");
let missing = body(&sha)
.lines()
.filter(|line| !line.contains("\"generated_at\""))
.collect::<Vec<_>>()
.join("\n");
assert!(Snapshot::parse(missing.as_bytes(), &sha, "ca").is_err());
let future = (Utc::now() + chrono::Duration::hours(1)).to_rfc3339();
let future = body(&sha).replace("2026-01-01T00:00:00Z", &future);
assert!(matches!(
Snapshot::parse(future.as_bytes(), &sha, "ca"),
Err(SnapshotError::FutureGeneration(_))
));
}
#[test]
fn normalizes_serials() {
assert_eq!(normalize_serial(&[0, 0, 1, 2]), vec![1, 2]);