use crate::keyring::{KeyError, KeyRing};
use super::conformance::Report;
pub async fn check(ring: &dyn KeyRing, scope: &str) -> Report {
let mut report = Report::default();
let at =
crate::core::Timestamp::from_unix_timestamp(1_760_000_000).expect("a valid test instant");
let Some(minted) = mint(ring, scope, &mut report).await else {
return report;
};
names_its_version(scope, &minted, &mut report);
erases(ring, scope, &minted, at, &mut report).await;
report
}
pub async fn check_isolated(ring: &dyn KeyRing, scope: &str, sibling: &str) -> Report {
let mut report = Report::default();
let at =
crate::core::Timestamp::from_unix_timestamp(1_760_000_000).expect("a valid test instant");
report.checked += 1;
let (doomed, kept) = match (ring.data_key(scope).await, ring.data_key(sibling).await) {
(Ok(a), Ok(b)) => (a, b),
(Err(e), _) | (_, Err(e)) => {
report.record("data_key mints under a realistic scope", format!("{e}"));
return report;
}
};
if let Err(e) = ring.destroy(scope, at, "conformance battery").await {
report.record("destroy erases a realistic scope", format!("{e}"));
return report;
}
report.checked += 1;
if !matches!(ring.open(&doomed.1).await, Err(KeyError::Destroyed { .. })) {
report.record(
"destroy erases a realistic scope",
format!("a key wrapped under '{scope}' still opens after its scope was destroyed"),
);
}
report.checked += 1;
match ring.open(&kept.1).await {
Ok(opened) if opened.expose() == kept.0.expose() => {}
Ok(_) => report.record(
"a sibling scope survives an erasure",
"the sibling's wrapped key opened to different material",
),
Err(e) => report.record(
"a sibling scope survives an erasure",
format!(
"destroying '{scope}' made '{sibling}' unopenable ({e}) — the two \
scopes share one key in the backend, so erasing one message erases \
another"
),
),
}
report
}
async fn mint(
ring: &dyn KeyRing,
scope: &str,
report: &mut Report,
) -> Option<(crate::keyring::DataKey, crate::keyring::WrappedKey)> {
report.checked += 1;
let first = match ring.data_key(scope).await {
Ok(k) => k,
Err(e) => {
report.record("data_key mints a key", format!("minting failed: {e}"));
return None;
}
};
let second = match ring.data_key(scope).await {
Ok(k) => k,
Err(e) => {
report.record(
"data_key mints a key",
format!("the second mint failed: {e}"),
);
return None;
}
};
report.checked += 1;
if first.0.expose() == second.0.expose() {
report.record(
"a data key is fresh per call",
"two mints returned the same key material. A service mints one per \
call; a ring that does not means two payloads in one scope share a \
key, and a caller written against a real KMS will not expect it",
);
}
report.checked += 1;
match ring.open(&first.1).await {
Ok(opened) if opened.expose() == first.0.expose() => {}
Ok(_) => report.record(
"open returns the key that was wrapped",
"opening a wrapped key produced different material, so nothing \
sealed with it can ever be read",
),
Err(e) => report.record("open returns the key that was wrapped", format!("{e}")),
}
Some(first)
}
fn names_its_version(
scope: &str,
minted: &(crate::keyring::DataKey, crate::keyring::WrappedKey),
report: &mut Report,
) {
report.checked += 1;
if minted.1.scope != scope {
report.record(
"a wrap names its erasure unit",
format!(
"the wrap claims scope '{}' but was minted for '{scope}', so \
erasing '{scope}' would destroy a key that does not reach the \
data this wrap seals — and report success",
minted.1.scope
),
);
}
report.checked += 1;
if minted.1.wrapped_by.is_empty() {
report.record(
"a wrap names the key version that sealed it",
"the wrap names no wrapping key. Sealed bytes are never re-wrapped, \
so this field is the only surviving record of which key version \
must stay decryptable for this payload to be readable; without it \
a retired version is indistinguishable from data loss",
);
}
}
async fn erases(
ring: &dyn KeyRing,
scope: &str,
minted: &(crate::keyring::DataKey, crate::keyring::WrappedKey),
at: crate::core::Timestamp,
report: &mut Report,
) {
report.checked += 1;
if let Err(e) = ring.destroy(scope, at, "conformance battery").await {
report.record("destroy erases a scope", format!("{e}"));
return;
}
report.checked += 1;
match ring.open(&minted.1).await {
Err(KeyError::Destroyed { .. }) => {}
Err(e) => report.record(
"an erased scope reports itself erased",
format!(
"opening after destruction failed with `{e}` rather than \
`Destroyed`. A caller cannot tell a completed erasure from an \
outage, and will either retry forever or report data loss"
),
),
Ok(_) => report.record(
"destroy erases a scope",
"a wrapped key still opened after its scope was destroyed, so the \
erasure reached nothing at all",
),
}
report.checked += 1;
match ring.data_key(scope).await {
Err(KeyError::Destroyed { .. }) => {}
Err(e) => report.record(
"an erased scope cannot be written to",
format!("minting after destruction failed with `{e}` rather than `Destroyed`"),
),
Ok(_) => report.record(
"an erased scope cannot be recreated",
"a destroyed scope minted a fresh key, so a late write lands in a \
unit already reported as erased and the next erasure finds data \
the last one said was gone",
),
}
report.checked += 1;
if let Err(e) = ring.destroy(scope, at, "a retry").await {
report.record(
"erasure is idempotent",
format!(
"a second destruction failed with `{e}`. Erasure is retried — by \
an operator, by a sweep, by a queue — and a retry that errors \
makes a completed erasure look unfinished"
),
);
}
}