trust-tasks-proof 0.23.2

Pluggable ProofVerifier implementations for the Trust Tasks framework. Backends are gated by Cargo features; ships with an Affinidi Data Integrity backend (EdDSA suites) by default.
docs.rs failed to build trust-tasks-proof-0.23.2
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: trust-tasks-proof-0.21.21

trust-tasks-proof

Pluggable ProofVerifier implementations for the Trust Tasks framework.

The framework's ProofVerifier trait is the seam where W3C Data Integrity cryptosuite implementations plug in; this crate is the umbrella that hosts those implementations behind Cargo features so a single dependency line opts you in to a specific backend without dragging in the others.

Backends

Cargo feature Module Backed by Cryptosuites
affinidi ✱ affinidi affinidi-data-integrity eddsa-rdfc-2022, eddsa-jcs-2022

✱ = enabled by default.

Why

The framework's SPEC §7.2 item 7 requires a conforming consumer to verify proof against the in-band issuer when present. trust-tasks-rs intentionally ships no cryptosuites — this crate hosts the concrete implementations.

Quickstart (affinidi backend)

use trust_tasks_proof::affinidi::Verifier;
use trust_tasks_rs::ProofVerifier;

// did:key only — offline, no I/O. Good for tests and self-issued docs.
let verifier = Verifier::for_did_key();
verifier.verify(&inbound_doc).await?;

For did:web / did:webvh / did:peer / did:jwk and the rest of the methods the Affinidi resolver cache handles, use CachedDidResolver:

use std::sync::Arc;
use affinidi_did_resolver_cache_sdk::{config::DIDCacheConfigBuilder, DIDCacheClient};
use trust_tasks_proof::affinidi::{CachedDidResolver, Verifier};

let client = DIDCacheClient::new(DIDCacheConfigBuilder::default().build()).await?;
let resolver = Arc::new(CachedDidResolver::new(Arc::new(client)));
let verifier = Verifier::with_resolver(resolver);

The default builder runs in local mode (no network) — sufficient for did:key, did:peer, and did:jwk. Add .with_network_mode(...) to point at a running resolver cache server for did:web, did:webvh, and the rest. See affinidi-did-resolver-cache-sdk for the full configuration surface.

The adapter currently extracts public keys from Multikey-typed verification methods (publicKeyMultibase); JWK-bearing methods surface a clean Resolver error so callers can stack a custom resolver in front.

Signing (affinidi backend)

Producers sign with sign_trust_task — the sign-side counterpart to the stock Verifier. It signs the document with the proof member removed (the exact canonicalisation contract the verify side applies), replaces any existing proof, and pre-flights the SPEC §4.7/§4.8 issuer binding (issuer must equal the DID of the signer's verificationMethod), so its output verifies with the stock Verifier by construction. Defaults: eddsa-jcs-2022, proofPurpose: authentication. The stock Verifier accepts a proof only when the issuer's DID document lists its verificationMethod under the relationship its proofPurpose names (keyAgreement never signs), so the signer's key must be listed under that relationship.

use trust_tasks_proof::affinidi::{sign_trust_task, SignOptions};

// `doc` is a serde_json::Value with `issuer` set to the signer's DID;
// `secret` is an affinidi-secrets-resolver Secret (or any upstream Signer).
let signed = sign_trust_task(&doc, &secret, SignOptions::new()).await?;

Signing a typed document — ProofExt

sign_trust_task works on serde_json::Value, which is the right shape for the primitive and the wrong one for a producer holding a TrustTask<P>. ProofExt is the typed wrapper: import the trait and both halves of the round-trip become methods on the document. It calls sign_trust_task internally, so the canonicalisation, the defaults and the issuer pre-flight are identical — a document signed either way is byte-for-byte the same.

use trust_tasks_proof::{affinidi::{SignOptions, Verifier}, ProofExt};
use trust_tasks_rs::{specs::acl::grant::v0_1 as grant, TrustTask};

let mut req = TrustTask::for_payload(new_id(), grant::Payload { /* … */ });
req.issuer = Some(my_did.clone());          // set every member first …
req.recipient = Some(server_did.clone());
req.sign(&secret, SignOptions::new()).await?;   // … then sign.

// Consumer side, same trait:
req.verify(&Verifier::for_did_key()).await?;

⚠ Sign last. The proof covers the document as it stands when sign is called; mutating any member afterwards invalidates the signature. Re-signing is always safe — the existing proof is discarded and a fresh one minted over the current content.

Opting out of the default backend

[dependencies]
trust-tasks-proof = { git = "...", default-features = false }
# add other backends here as they're introduced

With no features enabled the crate compiles to an empty surface — useful while you wait for a non-Affinidi backend to land, or if you're consuming the trait from trust-tasks-rs directly and only want the umbrella as a future home for adapters.

Error mapping (affinidi backend)

DataIntegrityError VerificationError SPEC §8.3 standard code
UnsupportedCryptoSuite UnsupportedCryptosuite proof_invalid
KeyTypeMismatch IssuerMismatch (descriptive) proof_invalid
InvalidSignature SignatureInvalid proof_invalid
InvalidPublicKey MalformedProof proof_invalid
MalformedProof MalformedProof proof_invalid
Canonicalization Other proof_invalid
other Other proof_invalid

A consumer pipeline that catches VerificationError converts it to a trust-task-error/0.1 document with code = proof_invalid (or code = proof_required if the proof was missing in the first place — that case is raised by the framework, not by this crate).

MSRV

1.94, matching affinidi-data-integrity 0.6.

Status

0.1.0, tracking SPEC.md 0.1. Round-trip-tested against affinidi-data-integrity's sign (see tests/round_trip.rs).