1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! [`ProofExt`] — `.sign()` and `.verify()` as methods on a typed
//! `TrustTask<P>`. Private module; the trait is re-exported at the crate
//! root and carries the documentation.
use async_trait;
use Serialize;
use ;
use crate;
use Proof;
/// Extension trait adding [`sign`](Self::sign) and
/// [`verify`](Self::verify) to the framework's [`TrustTask<P>`], for
/// every payload type `P` a producer can serialise.
///
/// # Why this trait exists
///
/// [`sign_trust_task`](crate::affinidi::sign_trust_task) operates on a
/// [`serde_json::Value`], because a W3C Data Integrity proof is computed
/// over the *document's JSON form* and the framework's document type is
/// generic over its payload. That is the right shape for the primitive
/// and the wrong shape for a producer, who holds a `TrustTask<P>` and
/// wants a signed `TrustTask<P>` back. Without this trait, signing is a
/// five-step ritual — serialise, call, check, deserialise, reassign —
/// that every producer writes out by hand and can get subtly wrong (most
/// often by mutating the document *after* signing it).
///
/// `ProofExt` is a thin typed wrapper over the free functions, not a
/// replacement for them. It reuses
/// [`sign_trust_task`](crate::affinidi::sign_trust_task) verbatim, so the
/// canonicalisation contract, the deterministic `eddsa-jcs-2022` default,
/// the replace-don't-nest rule for an existing proof, and the SPEC.md
/// §4.7/§4.8 issuer↔`verificationMethod` pre-flight are all exactly what
/// that function already implements. A document signed through this trait
/// and one signed through the free function are byte-identical.
///
/// ```rust,ignore
/// 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 at the moment
/// [`sign`](Self::sign) is called. Mutating any member afterwards
/// invalidates the signature, and nothing in the type system stops you —
/// `sign` takes `&mut self` precisely so the call reads as the final step
/// of composing the document. Re-signing after a change is always safe:
/// the existing proof is discarded and a fresh one minted over the
/// current content.