pub struct VerifiedContext { /* private fields */ }Expand description
A retrieved context that has been cryptographically verified.
Every value of this type is the output of one of the
VerifiedContext::fetch* pipelines, each of which independently
recomputes content_hash (RFC-ACDP-0001 §5.11) and verifies the
producer signature before the value is constructed. The fields are
private precisely so this “cryptographically verified” invariant
cannot be forged: there is no way to construct a VerifiedContext
around an unverified FullContext. Downstream code can therefore
trust the accessors below without re-deriving anything.
Implementations§
Source§impl VerifiedContext
impl VerifiedContext
Sourcepub async fn fetch(
client: &RegistryClient,
resolver: &WebResolver,
ctx_id: &CtxId,
) -> Result<Self, AcdpError>
pub async fn fetch( client: &RegistryClient, resolver: &WebResolver, ctx_id: &CtxId, ) -> Result<Self, AcdpError>
Retrieve a context and verify its signature using the strict
default VerificationPolicy.
Sourcepub async fn fetch_with_policy(
client: &RegistryClient,
resolver: &WebResolver,
ctx_id: &CtxId,
policy: &VerificationPolicy,
) -> Result<Self, AcdpError>
pub async fn fetch_with_policy( client: &RegistryClient, resolver: &WebResolver, ctx_id: &CtxId, policy: &VerificationPolicy, ) -> Result<Self, AcdpError>
Retrieve a context and verify its signature with caller-controlled strictness.
- Fetches
body + registry_statefrom the registry. - Refuses a served body whose
ctx_iddiffers from the one requested (AcdpError::ContextIdMismatch) — this implements RFC-ACDP-0006 §4.1 step 7 (NORMATIVE, “Bind the resolved identity”): neither the signature check (step 5) nor thecontent_hashrecomputation (step 6) can supply this binding, becausectx_idsits in the RFC-ACDP-0001 §5.7 registry-assigned exclusion set and is therefore stripped from ProducerContent before hashing. See RFC-ACDP-0008 §9.1 for the threat this closes: without it, a registry can serve any other validly-signed body by the same producer under the requested context’s URL, and both preceding checks still pass. Step 7 permits a consumer to surface “an equivalent typed error” in place of the registry-sidecross_registry_resolution_failedwire code —ContextIdMismatchis that typed error. This generalizes the receipt-path analogue at RFC-ACDP-0010 §8 step 3 to the receipt-less core-profile path, where it is the only binding available. It does not close §9.1 in full: a registry that genuinely republishes the same content under a newctx_idstill passes; only serve-time substitution — a different id claimed to be the one requested — is caught. - Optionally runs
validate_body— structural schema checks plus embedded-DataRefhash verification (policy-controlled). - Recomputes
content_hashover ProducerContent. - Resolves the producer’s DID document.
did:webis required unconditionally for v0.1.0 (RFC-ACDP-0001 §5.4). - Verifies the Ed25519 signature (or other supported algorithm).
- Optionally verifies the
registry_receiptplaceholder. - Optionally rejects unknown statuses.
Sourcepub async fn fetch_current(
client: &RegistryClient,
resolver: &WebResolver,
lineage_id: &LineageId,
) -> Result<Self, AcdpError>
pub async fn fetch_current( client: &RegistryClient, resolver: &WebResolver, lineage_id: &LineageId, ) -> Result<Self, AcdpError>
Retrieve the current head of a lineage
(GET /lineages/{lineage_id}/current) and verify it with the
strict default VerificationPolicy — including the
lineage-head receipt when the registry minted one (ACDP 0.3,
RFC-ACDP-0011).
Sourcepub async fn fetch_current_with_policy(
client: &RegistryClient,
resolver: &WebResolver,
lineage_id: &LineageId,
policy: &VerificationPolicy,
) -> Result<Self, AcdpError>
pub async fn fetch_current_with_policy( client: &RegistryClient, resolver: &WebResolver, lineage_id: &LineageId, policy: &VerificationPolicy, ) -> Result<Self, AcdpError>
Retrieve + verify the current head of a lineage with caller-controlled strictness.
Runs the same pipeline as Self::fetch_with_policy against
the /current response (the expected ctx_id is the served
body’s own — there is no requested identifier on this endpoint;
the head receipt’s §7 step 5 byte-match is what binds it), then
applies policy.lineage_head to the response’s
lineage_head_receipt per RFC-ACDP-0011 §7:
ReceiptPolicy::Ignore— the raw value is preserved verbatim, unverified.ReceiptPolicy::VerifyIfPresent— verified when present (absence is fine: the registry may not advertiseacdp-registry-head-receipts).ReceiptPolicy::Require— fail closed withinvalid_receiptunless present AND verified.
Verification fetches the registry’s capabilities document for
the §7 step 3 capabilities.registry_did binding. Staleness
beyond policy.lineage_head.max_age_seconds is a freshness
verdict reported via Self::head_receipt_stale, never a
verification failure (§6).
Self::fetch_with_policy now additionally refuses a served body
whose ctx_id is not the one requested (RFC-ACDP-0008 §9.1). This
endpoint has no requested identifier to compare against — the
served head’s ctx_id is trivially “the one requested” — so on a
receipt-less registry the served head’s identity rests entirely on
registry honesty (RFC-ACDP-0008 §9.1). Use ReceiptPolicy::Require
where that matters.
Sourcepub async fn fetch_report(
client: &RegistryClient,
resolver: &WebResolver,
ctx_id: &CtxId,
policy: &VerificationPolicy,
) -> Result<(Self, VerificationReport), AcdpError>
pub async fn fetch_report( client: &RegistryClient, resolver: &WebResolver, ctx_id: &CtxId, policy: &VerificationPolicy, ) -> Result<(Self, VerificationReport), AcdpError>
Retrieve + verify, returning a structured VerificationReport
alongside the verified context. Does NOT attempt external
DataRef fetches — use Self::fetch_report_with_fetcher for
that. Each data_ref_external slot in the returned report is
None.
Unlike Self::fetch_with_policy, per-DataRef embedded-hash
failures are recorded in the report instead of aborting the
verification. The top-level checks (schema, body hash,
signature) remain hard-fail: if any of them fails, the method
returns an AcdpError and produces no report.
For diagnostic callers that want a populated report even when
a top-level check fails (e.g. an audit walker that needs to
distinguish “wrong hash” from “wrong signature”), use
Self::fetch_report_diagnose instead.
Sourcepub async fn fetch_report_diagnose(
client: &RegistryClient,
resolver: &WebResolver,
ctx_id: &CtxId,
policy: &VerificationPolicy,
) -> Result<(Option<Self>, VerificationReport), AcdpError>
pub async fn fetch_report_diagnose( client: &RegistryClient, resolver: &WebResolver, ctx_id: &CtxId, policy: &VerificationPolicy, ) -> Result<(Option<Self>, VerificationReport), AcdpError>
Diagnostic variant of Self::fetch_report that never
short-circuits on a top-level failure — schema, body-hash, and
signature outcomes are each recorded individually in the
returned VerificationReport. Returns Ok((None, report))
when any top-level probe failed (the report shows which one);
Ok((Some(verified), report)) only when every check passed
(FEAT-05) — and “every check” now genuinely means every
authorization phase (receipt, revocation, signature/
historical-key, unknown-status), not just the top-level probes:
once the probes pass, this method additionally runs the same
verify_retrieved phase fetch_with_policy does, and withholds
the handle — recording the cause in
VerificationReport::policy_phase_error — if that phase fails
too. Either way the method still returns Ok; it never converts
a policy-phase failure into an Err.
Use cases:
- Audit walkers that need to classify failures by stage.
- Admin tooling that wants to distinguish “hash mismatch” (probable tampering / encoding drift) from “signature verification failed” (key compromise / DID resolution problem).
Network errors from the initial retrieval still propagate as
Err — there’s no body to inspect when the registry is
unreachable. But network/DID-resolution errors that occur
inside the verify_retrieved phase (e.g. resolving the
fingerprint for a receipt cross-check, or the historical-key
fallback) are caught there and land in
VerificationReport::policy_phase_error instead of Err,
same as any other phase failure — this method never
short-circuits once retrieval has succeeded. That means a
transient network flake at that stage can read as a policy
rejection (Ok((None, report))) rather than an Err. A caller
that needs to tell a flake from a genuine rejection should
inspect policy_phase_error’s AcdpError::is_transient.
Stage-classification caveat: verify_retrieved (and therefore the
receipt/revocation/signature/historical-key phases it runs) is only
attempted once ALL top-level probes pass, ctx_id_ok included — see
all_top_level_pass below. So when ctx_id_ok is false,
policy_phase_error stays None even if the served body also
carries a receipt that would independently fail its own cross-check
(e.g. a receipt genuinely bound to the served, substituted body —
RFC-ACDP-0010 §8 step 3 — rather than the requested one). A caller
classifying a ctx_id_ok: false report should not read a None
policy_phase_error as “the receipt/signature phases passed” — they
were never attempted.
Sourcepub async fn fetch_report_with_fetcher<F: DataRefFetcher>(
client: &RegistryClient,
resolver: &WebResolver,
ctx_id: &CtxId,
policy: &VerificationPolicy,
fetcher: &F,
) -> Result<(Self, VerificationReport), AcdpError>
pub async fn fetch_report_with_fetcher<F: DataRefFetcher>( client: &RegistryClient, resolver: &WebResolver, ctx_id: &CtxId, policy: &VerificationPolicy, fetcher: &F, ) -> Result<(Self, VerificationReport), AcdpError>
Retrieve + verify like Self::fetch_report, and additionally
fetch every DataRef whose location resolves through fetcher.
Each external fetch outcome is recorded in report.data_ref_external.
pub fn body(&self) -> &Body
pub fn registry_state(&self) -> &RegistryState
Sourcepub fn full_context(&self) -> &FullContext
pub fn full_context(&self) -> &FullContext
The verified FullContext (body + registry state + any
receipts) in its retrieval shape. Every field was reached only
after this context’s hash + signature were verified.
Sourcepub fn key_status(&self) -> KeyAuthorization
pub fn key_status(&self) -> KeyAuthorization
Whether the body verified against a currently authorized key, a
receipt-attested historical one, or a receipt-attested
pre-compromise one (ACDP 0.2 WS-B / RFC-ACDP-0014 §7). This is
the real verdict regardless of which fetch*/fetch_report*
entry point produced this VerifiedContext — every construction
path runs the same verify_retrieved phase to derive it.
Sourcepub fn verified_receipt(&self) -> Option<&RegistryReceipt>
pub fn verified_receipt(&self) -> Option<&RegistryReceipt>
The verified registry receipt (RFC-ACDP-0010), when one was
present and the policy verified it. None under
ReceiptPolicy::Ignore or when the registry minted none — this
is exhaustive; there is no additional “or you used a report path”
carve-out, since fetch_report/fetch_report_with_fetcher/
fetch_report_diagnose verify the receipt exactly like
fetch_with_policy does. For the raw on-wire value see
Self::receipt.
Sourcepub fn verified_head_receipt(&self) -> Option<&LineageHeadReceipt>
pub fn verified_head_receipt(&self) -> Option<&LineageHeadReceipt>
The verified lineage-head receipt (ACDP 0.3, RFC-ACDP-0011),
populated only by Self::fetch_current /
Self::fetch_current_with_policy when one was present and the
policy verified it. For the raw on-wire value see
Self::lineage_head_receipt.
Sourcepub fn head_receipt_stale(&self) -> Option<bool>
pub fn head_receipt_stale(&self) -> Option<bool>
RFC-ACDP-0011 §6 freshness verdict for the verified head
receipt: Some(true) when the (genuine, verified) receipt’s
as_of is older than LineageHeadPolicy::max_age_seconds;
Some(false) when within policy; None when there is no
verified head receipt or the max-age knob is disabled.
Sourcepub fn revocation_discovery_failure(&self) -> Option<&AcdpError>
pub fn revocation_discovery_failure(&self) -> Option<&AcdpError>
RFC-ACDP-0014 §8 auto-discovery failure that
DiscoveryFailurePolicy::ProceedWithKnown swallowed to let
this VerifiedContext exist at all. None when discovery was
off (RevocationPolicy::discover is None), succeeded, or
was never attempted — a DiscoveryFailurePolicy::FailClosed
failure turns into this call’s Err instead, so there is no
VerifiedContext to carry it in that case. See
VerificationReport::revocation_discovery for the twin
surface on the report family, populated from the same event on
the fetch_report* paths.
Sourcepub fn receipt(&self) -> Option<&Value>
pub fn receipt(&self) -> Option<&Value>
Raw registry receipt value as served on the wire
(RFC-ACDP-0010), preserved verbatim. For the verified, typed
form see Self::verified_receipt.
Sourcepub fn lineage_head_receipt(&self) -> Option<&Value>
pub fn lineage_head_receipt(&self) -> Option<&Value>
Raw lineage-head receipt value as served on the wire
(RFC-ACDP-0011), preserved verbatim. For the verified, typed
form see Self::verified_head_receipt.
Sourcepub async fn verify_receipt(
&self,
resolver: &WebResolver,
) -> Result<Option<RegistryReceipt>, AcdpError>
pub async fn verify_receipt( &self, resolver: &WebResolver, ) -> Result<Option<RegistryReceipt>, AcdpError>
Verify the registry receipt, when one is present (RFC-ACDP-0010).
Standalone variant for contexts obtained via the report paths;
fetch_with_policy already does this under
ReceiptPolicy::VerifyIfPresent/Require. The serving
authority is taken from the context’s own ctx_id — this method
performs no requested-id binding of its own (it has no requested
id to compare against; it only ever sees self.inner.body.ctx_id),
so deriving the serving authority this way is sound only for a
VerifiedContext obtained through a pipeline that already bound
the served ctx_id to the one requested. Every construction path
does: fetch_with_policy and CrossRegistryResolver::resolve
check it directly; fetch_current_with_policy does too,
tautologically, since /current has no requested id to diverge
from; fetch_report/fetch_report_with_fetcher check it and
return ContextIdMismatch on failure; and fetch_report_diagnose
folds it into its all_top_level_pass gate, so it only ever
hands back Some(VerifiedContext) when ctx_id_ok held. All of
these implement RFC-ACDP-0006 §4.1 step 7, so the type invariant
— every VerifiedContext was bound to its requested ctx_id —
holds unconditionally.
Returns Ok(None) when no receipt is present, Ok(Some(_))
with the verified receipt otherwise.
The receipt cross-check (RFC-ACDP-0010 §8 step 4) relies on
body.content_hash being the independently recomputed value.
That is guaranteed by the type invariant — every
VerifiedContext is built only after its constructing pipeline
verified the body hash (Verifier::verify_body_hash /
verify_body_signed), and the fields are private so no caller
can substitute an unverified body — so no re-derivation is
needed here.