trust_tasks_proof 0.1.7
trust_tasks_proof: ^0.1.7 copied to clipboard
W3C Data Integrity proofs for Trust Tasks: a ProofVerifier for package:trust_tasks and the signer that produces what it verifies, on ssi.
// Verifying a signed Trust Task document, inside the §7.2 pipeline.
//
// Run it:
// dart run example/main.dart
//
// The document below was signed by trust-tasks-proof, the Rust library, with
// eddsa-jcs-2022 under a did:key. Verifying it here needs no network: a did:key
// carries its own public key.
// The document is the cross-library eddsa-jcs-2022 fixture, which four
// libraries reproduce byte for byte, so it is never re-signed. It is an
// acl/grant 0.1 document, a version past its generated library's lifetime, so
// the example consumes it in the generic form — a JSON payload and the
// specification's policy stated directly — rather than through generated types.
// A proof verifies over JSON, so nothing about the verification changes.
import 'dart:convert';
import 'package:trust_tasks/trust_tasks.dart';
import 'package:trust_tasks_proof/trust_tasks_proof.dart';
const me = 'did:web:maintainer.example';
const peer = 'did:key:z6MkvDqGT54cXesYGvABpF1UapVNwjCqRcafi4Px6Thv5T3Z';
/// acl/grant 0.1's policy: proof, recipient and issuedAt are all REQUIRED.
const aclGrant01 = SpecPolicy(
typeUri: 'https://trusttasks.org/spec/acl/grant/0.1',
isProofRequired: true,
isRecipientRequired: true,
isIssuedAtRequired: true,
);
typedef Json = Map<String, dynamic>;
const signedJson = '''
{
"id": "urn:uuid:interop-1",
"type": "https://trusttasks.org/spec/acl/grant/0.1",
"issuer": "$peer",
"recipient": "$me",
"issuedAt": "2026-01-01T00:00:00Z",
"payload": {"entry": {"role": "admin", "subject": "did:web:alice.example"}},
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"created": "2026-09-16T10:34:46Z",
"proofPurpose": "assertionMethod",
"verificationMethod": "$peer#z6MkvDqGT54cXesYGvABpF1UapVNwjCqRcafi4Px6Thv5T3Z",
"proofValue": "zbBjAshSrFcq3jaNSykHWRSmzNgGPU9DsPYFar8mLy2183ZfUtKTyz8B1nN6WnBdWEkXjT3MiYJvMnHsT1aEMrCK"
}
}
''';
Future<void> main() async {
// did:key only, no I/O. Use DataIntegrityProofVerifier.withResolver to accept
// did:web and other methods.
final verifier = DataIntegrityProofVerifier.forDidKey();
Future<void> deliver(String label, Map<String, dynamic> json) async {
// verifyDetailed says *why* a proof failed. Log it; never send it — the
// pipeline answers the peer with a bare proofInvalid (SPEC §12.4).
print('$label: ${await verifier.verifyDetailed(json)}');
final outcome = await consumeInbound<Json, Json>(
transport: const StaticTransport(TransportContext(issuer: peer)),
spec: aclGrant01,
proofPolicy: ProofPolicy.verify(verifier),
payloadPolicy: const PayloadPolicy.acceptUnvalidated(),
checks: notConsequentialChecks(),
doc: TrustTaskDocument<Json>.fromJson(json, (p) => p! as Json),
// Verify the proof over the document exactly as it arrived.
received: json,
myVid: me,
// Fixed so the example's issuedAt stays inside the freshness window. The
// verifier keeps its own clock for the proof's `created`; pass `clock:`
// to forDidKey to pin that too.
now: DateTime.parse('2026-01-01T00:00:30Z'),
newErrorId: () => 'urn:uuid:9b2c1e34-0000-4000-8000-0000000000ff',
payloadToJson: (p) => p,
handler: (accepted, parties) async => respondWith<Json, Json>(
accepted,
'urn:uuid:9b2c1e34-0000-4000-8000-000000000002',
{'entry': accepted.payload['entry']},
),
);
print(' -> ${switch (outcome) {
Handled(:final response) => 'handled, ${response.type}',
Rejected(:final error) => 'rejected, ${error.payload.code}',
_ => '$outcome',
}}');
}
final signed = jsonDecode(signedJson) as Map<String, dynamic>;
await deliver('as signed', signed);
final escalated = jsonDecode(signedJson) as Map<String, dynamic>;
(escalated['payload'] as Map<String, dynamic>)['entry'] = {
'role': 'owner',
'subject': 'did:web:alice.example',
};
await deliver('role changed in transit', escalated);
}