#[non_exhaustive]pub struct RevocationDiscovery {
pub include_registry_attested: bool,
pub on_failure: DiscoveryFailurePolicy,
pub total_timeout: Duration,
}Expand description
RFC-ACDP-0014 §8 revocation auto-discovery configuration.
When set on RevocationPolicy::discover, this instructs
verification to look up revocations itself — via
find_revocations and,
when Self::include_registry_attested is true, additionally
find_registry_attested_revocations
— instead of relying solely on RevocationPolicy::known.
§Cost
Discovery is expensive, and every request it issues is serial.
MAX_SEARCH_PAGES = 10 (crate::revocation) bounds search
round-trips per (type_form, status) pair, and there are 6
such pairs (2 type forms × 3 statuses) — so up to 60 search
requests, each of which can name up to 100 per-candidate context
retrieves (GET /contexts/{id}, capped at 1 MB apiece), for up to
6,000 + 60 + 100 = 6,160 requests / ~6.1 GB in the worst case
for one of the two discovery functions. The retrieve fan-out is
not bounded by MAX_LINEAGE_WALKS = 100 — that cap is only
checked after the retrieves have already gone out. With
Self::include_registry_attested set, both functions run:
≈12,321 requests / ~12.2 GB worst case for the pair (the extra
1 is the unconditional client.capabilities() fetch
find_registry_attested_revocations makes).
Self::total_timeout is an availability bound, not a bytes or
memory bound: it stops verification from hanging forever against
a slow or hostile registry, but a hostile registry on a fast link
can still serve gigabytes of legitimate-looking traffic inside the
window — the 1 MB cap applies per request, not in aggregate, and
verified revocations accumulate in a Vec for the call’s duration.
There is no request-count or byte budget in this version, and no
cache: every call re-discovers from scratch. A caller verifying
many contexts against the same producer should discover once
itself and pass the results via RevocationPolicy::known instead
of setting discover on every call — the same hoisting guidance
crate::revocation’s find_registry_attested_revocations doc
already gives callers of that function directly (see its “Cost
note for callers verifying many contexts”).
§Reentrancy
Discovery calls back into verification, and that reentrancy has two consequences worth stating explicitly rather than leaving implicit:
- Each candidate body
find_revocationsturns up is verified viaVerifier::new(resolver).verify_body— notverify_retrieved— so discovered revocation bodies are themselves checked without revocation checking of their own. That is defensible under RFC-ACDP-0014 §5 step 1’s “currently authorized key,” but it is an assumption this type is making on the caller’s behalf, not an accident. - Verifying a
key-revocationcontext now also triggers discovery against the same producer, so the revocation-fetch path itself becomes fragile underDiscoveryFailurePolicy::FailClosed: a producer whose revocation search is briefly unreachable can no longer be verified as revoked, either.
§No Default
This type deliberately has no Default impl — construct it
via Self::producer_signed_only or Self::all_trust_classes.
RFC-ACDP-0014 §6’s “lost-everything” fallback means a producer that
has lost every key it could sign a revocation with can only be
revoked registry-attested — so the catastrophic case is exactly the
one a silently-defaulted-off trust class would skip. A quiet
Default::default() that leaves include_registry_attested: false
would make that skip invisible at every call site; forcing a named
constructor puts the choice at the type level instead, where a
reviewer (and git grep) can see it. Callers protecting against
key loss, or otherwise unwilling to assume a producer always
retains signing capacity, MUST use Self::all_trust_classes.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.include_registry_attested: boolWhether to also run
find_registry_attested_revocations
(the §6 registry-attested trust class), in addition to the
producer-signed search every discovery configuration runs.
Requires the registry to serve /.well-known/acdp.json; under
DiscoveryFailurePolicy::FailClosed a registry that serves no
capabilities document fails verification when this is true.
on_failure: DiscoveryFailurePolicyWhat to do when discovery itself fails (a transient transport
error from either search, or the search-safety-cap error
AcdpError::SearchTruncated). Default DiscoveryFailurePolicy::FailClosed.
SearchTruncated and a transport error (e.g. a 503) are NOT
equivalent, even though both take this same on_failure path.
SearchTruncated means “this producer has more revocations than
we will page through” (MAX_SEARCH_PAGES) — an
attacker-inducible security downgrade, since a hostile
producer or registry can pad the result set specifically to
exhaust the page cap and hide a real revocation from discovery.
A 503 is an ordinary availability blip. Under
DiscoveryFailurePolicy::ProceedWithKnown both are treated the
same way (proceed on RevocationPolicy::known alone, record
the failure) — choose ProceedWithKnown knowing it also waives
truncation, not just transient unavailability.
total_timeout: DurationWall-clock budget for the whole discovery step (both searches,
if Self::include_registry_attested is set). An availability
bound only — see the type-level cost section above. Matches this
crate’s existing ResolverOptions::total_timeout precedent
(crate::cross_registry), defaulting to the same 30 s rather
than exceeding it on the core verify path.
Enforced via tokio::time::timeout, which requires the
executing Tokio runtime to have its time driver enabled
(#[tokio::main] and #[tokio::test] enable it by default;
a hand-built Builder::new_current_thread() runtime does
not unless .enable_time() or .enable_all() is called).
Calling verify_retrieved with discover: Some(..) from a
runtime without the time driver panics — it does not
return Err — the same requirement ResolverOptions::total_timeout
(crate::cross_registry) already carries on its opt-in walk,
but here it sits on the core verify path whenever discovery is
configured, not just on an explicit cross-registry walk.
Implementations§
Source§impl RevocationDiscovery
impl RevocationDiscovery
Sourcepub fn producer_signed_only() -> Self
pub fn producer_signed_only() -> Self
Discover producer-signed revocations only
(include_registry_attested: false). Cheapest of the two
constructors, and the default choice for callers that are not
specifically defending against a producer that has lost every
signing key — see the “No Default” section above for who must
NOT stop here.
Sourcepub fn all_trust_classes() -> Self
pub fn all_trust_classes() -> Self
Discover both trust classes: producer-signed AND registry-attested
(include_registry_attested: true). Required to catch RFC-ACDP-0014
§6’s “lost-everything” fallback, where a producer with no signing
key left can only be revoked registry-attested. Requires the
registry to serve a capabilities document.
Trait Implementations§
Source§impl Clone for RevocationDiscovery
impl Clone for RevocationDiscovery
Source§fn clone(&self) -> RevocationDiscovery
fn clone(&self) -> RevocationDiscovery
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for RevocationDiscovery
Source§impl Debug for RevocationDiscovery
impl Debug for RevocationDiscovery
impl Eq for RevocationDiscovery
Source§impl PartialEq for RevocationDiscovery
impl PartialEq for RevocationDiscovery
impl StructuralPartialEq for RevocationDiscovery
Auto Trait Implementations§
impl Freeze for RevocationDiscovery
impl RefUnwindSafe for RevocationDiscovery
impl Send for RevocationDiscovery
impl Sync for RevocationDiscovery
impl Unpin for RevocationDiscovery
impl UnsafeUnpin for RevocationDiscovery
impl UnwindSafe for RevocationDiscovery
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.