Skip to main content

VerifiedContext

Struct VerifiedContext 

Source
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

Source

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.

Source

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.

  1. Fetches body + registry_state from the registry.
  2. Refuses a served body whose ctx_id differs 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 the content_hash recomputation (step 6) can supply this binding, because ctx_id sits 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-side cross_registry_resolution_failed wire code — ContextIdMismatch is 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 new ctx_id still passes; only serve-time substitution — a different id claimed to be the one requested — is caught.
  3. Optionally runs validate_body — structural schema checks plus embedded-DataRef hash verification (policy-controlled).
  4. Recomputes content_hash over ProducerContent.
  5. Resolves the producer’s DID document. did:web is required unconditionally for v0.1.0 (RFC-ACDP-0001 §5.4).
  6. Verifies the Ed25519 signature (or other supported algorithm).
  7. Optionally verifies the registry_receipt placeholder.
  8. Optionally rejects unknown statuses.
Source

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).

Source

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:

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.

Source

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.

Source

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.

Source

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.

Source

pub fn body(&self) -> &Body

Source

pub fn registry_state(&self) -> &RegistryState

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Debug for VerifiedContext

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more