From 1e2888e75ad8a0f78247723546b7852434260851 Mon Sep 17 00:00:00 2001 From: AtHeartEngineer <1675654+AtHeartEngineer@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:06:47 -0400 Subject: [PATCH 1/3] style(db): wrap test assertion to satisfy rustfmt cargo fmt --all --check on main flags the active_key assertion in the rotate-rollback test; this is the mechanical rustfmt wrap, no behavior change. --- src/db.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/db.rs b/src/db.rs index 5238c04..8ace052 100644 --- a/src/db.rs +++ b/src/db.rs @@ -441,7 +441,10 @@ mod tests { let r = db.rotate_key_sealed("g1", b"spki-2", |_id| Err("seal boom".to_string())); assert!(r.is_err()); - let after = db.active_key("g1").unwrap().expect("original key still active"); + let after = db + .active_key("g1") + .unwrap() + .expect("original key still active"); assert_eq!(after.key_id, before.key_id, "the original key stays active"); let opened = kek.open("g1", after.key_id, &after.sealed_pkcs8).unwrap(); assert_eq!(opened, b"secret-1"); From 14f9473243d1dc31350340ef204bbe8939ce0c9e Mon Sep 17 00:00:00 2001 From: AtHeartEngineer <1675654+AtHeartEngineer@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:06:47 -0400 Subject: [PATCH 2/3] test(keygen_dos): raise poll ceiling to 120s to match other suites The two concurrency tests (distinct-group bound, same-group dedup) fail on shared CI runners: poll_until_ready capped eventual-readiness at 600 x 25ms (~15s), but the test binary runs its tests in parallel, so dozens of 1024-bit safe-prime keygens contend for a few vCPUs and a group can take well over 15s to come ready. at_rest and issuance already poll at 1200 x 100ms (~120s) and pass on the same runners. Align the ceiling to the same 1200 x 100ms. The DoS bound the suite exists for is asserted by the immediate 200/202 enqueue responses and the single-stable-key-id dedup check, not by this eventual-readiness poll, so the longer ceiling weakens no security property. --- tests/keygen_dos.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/keygen_dos.rs b/tests/keygen_dos.rs index fee44d6..742b29b 100644 --- a/tests/keygen_dos.rs +++ b/tests/keygen_dos.rs @@ -25,8 +25,15 @@ use std::time::Duration; const FAST_BITS: usize = 1024; /// Poll `GET /key` until ready (200) or timeout. Returns the final status code. +/// +/// The ceiling (1200 x 100ms ~ 120s) matches the other integration suites +/// (at_rest, issuance): on shared CI runners the test binary runs its tests in +/// parallel, so many safe-prime keygens contend for a few vCPUs and a single +/// group can legitimately take well over 15s to come ready. The DoS bound is +/// asserted by the immediate 200/202 responses, not by this eventual-readiness +/// poll, so a generous ceiling weakens nothing. async fn poll_until_ready(client: &reqwest::Client, base: &str, group: &str) -> bool { - for _ in 0..600 { + for _ in 0..1200 { let res = client .get(format!("{base}/key?group_id={group}")) .send() @@ -37,7 +44,7 @@ async fn poll_until_ready(client: &reqwest::Client, base: &str, group: &str) -> assert_eq!(body["status"], "ready"); return true; } - tokio::time::sleep(Duration::from_millis(25)).await; + tokio::time::sleep(Duration::from_millis(100)).await; } false } From 52cb1d32b019928726ba99de11c5ac1d82f0e42c Mon Sep 17 00:00:00 2001 From: AtHeartEngineer <1675654+AtHeartEngineer@users.noreply.github.com> Date: Sat, 4 Jul 2026 12:31:20 -0400 Subject: [PATCH 3/3] test: raise keygen poll ceilings to 360s across integration suites The first fix raised only keygen_dos to the 120s ceiling the other suites used, but the PR's CI run then failed in issuance on a slower runner: three of its five tests panicked with 'key never became ready' at the 120s ceiling. Root cause is shared by all three suites: each test spawns its own server and 2048-bit safe-prime keygen (the pbrsa crate rejects moduli under 2048, so a smaller test modulus is not an option on the signing path), the binary runs its tests in parallel, and safe-prime generation is high-variance. On that runner a single uncontended keygen took ~50-95s, so five concurrent ones cannot all fit inside 120s windows on ~4 vCPUs. Raise the eventual-readiness ceilings in issuance, at_rest, and keygen_dos to 3600 x 100ms (~360s). The polls return as soon as the key is ready, so a healthy run pays nothing; a degraded runner spends wall clock instead of failing. No assertion is weakened and no test is skipped. --- tests/at_rest.rs | 6 ++++-- tests/issuance.rs | 8 +++++++- tests/keygen_dos.rs | 15 ++++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tests/at_rest.rs b/tests/at_rest.rs index a85cb2c..589864f 100644 --- a/tests/at_rest.rs +++ b/tests/at_rest.rs @@ -37,9 +37,11 @@ async fn private_key_is_ciphertext_at_rest() { res.status() == 202 || res.status() == 200, "POST /key should enqueue (202) or be already-ready (200)" ); - // Poll until ready (generous ceiling for slow release keygen). + // Poll until ready. Ceiling 3600 x 100ms ~ 360s: 2048-bit safe-prime + // keygen is high-variance and a single key has taken ~50s on a slow + // shared CI runner; the poll exits as soon as the key is ready. let mut ready = false; - for _ in 0..1200 { + for _ in 0..3600 { let res = client .get(format!("{base}/key?group_id={g}")) .send() diff --git a/tests/issuance.rs b/tests/issuance.rs index 5533573..3f1ce0c 100644 --- a/tests/issuance.rs +++ b/tests/issuance.rs @@ -26,8 +26,14 @@ fn info(version: &str) -> Vec { /// Fetch the active public key, polling `GET /key` until the async keygen /// reports the key ready. The first call typically returns 202 pending. +/// +/// Ceiling 3600 x 100ms ~ 360s: all five tests here run in parallel, each with +/// its own 2048-bit safe-prime keygen (the scheme rejects smaller moduli), so +/// on a slow shared CI runner a single key can take minutes to come ready. The +/// poll exits as soon as the key is ready; the ceiling only spends wall clock +/// on degraded runners instead of failing. async fn fetch_pubkey(client: &reqwest::Client, base: &str, group: &str) -> PubKey { - for _ in 0..1200 { + for _ in 0..3600 { let res = client .get(format!("{base}/key?group_id={group}")) .send() diff --git a/tests/keygen_dos.rs b/tests/keygen_dos.rs index 742b29b..bdb0c28 100644 --- a/tests/keygen_dos.rs +++ b/tests/keygen_dos.rs @@ -26,14 +26,15 @@ const FAST_BITS: usize = 1024; /// Poll `GET /key` until ready (200) or timeout. Returns the final status code. /// -/// The ceiling (1200 x 100ms ~ 120s) matches the other integration suites -/// (at_rest, issuance): on shared CI runners the test binary runs its tests in -/// parallel, so many safe-prime keygens contend for a few vCPUs and a single -/// group can legitimately take well over 15s to come ready. The DoS bound is -/// asserted by the immediate 200/202 responses, not by this eventual-readiness -/// poll, so a generous ceiling weakens nothing. +/// The ceiling (3600 x 100ms ~ 360s) matches the other integration suites +/// (at_rest, issuance): safe-prime keygen is high-variance, the binary runs its +/// tests in parallel, and shared CI runners are slow, so many concurrent +/// keygens contending for a few vCPUs can legitimately take minutes. The poll +/// exits as soon as the key is ready, so the ceiling costs nothing on a healthy +/// run. The DoS bound is asserted by the immediate 200/202 responses, not by +/// this eventual-readiness poll, so a generous ceiling weakens nothing. async fn poll_until_ready(client: &reqwest::Client, base: &str, group: &str) -> bool { - for _ in 0..1200 { + for _ in 0..3600 { let res = client .get(format!("{base}/key?group_id={group}")) .send()