trust_tasks_didcomm_v1/handler.rs
1//! [`DidcommV1Handler`] — the framework's [`TransportHandler`] for DIDComm v1.
2//!
3//! Mirrors `trust-tasks-didcomm`'s handler for v2.1, and reaches the same
4//! outcome by a different route. A v2.1 envelope authenticates a **DID**
5//! directly: the verified `sender_kid` reduces to one. A v1 envelope contains no
6//! DID at all — it authenticates a bare base58 Ed25519 **verkey**, and the
7//! verkey-to-DID binding is connection state the agent holds, not something the
8//! wire carries.
9//!
10//! So the transport-authenticated sender SPEC.md §4.8.1 needs is the
11//! connection's `theirDid`, resolved by the agent before this handler is built.
12//! Where the agent holds no binding for the authenticating verkey the envelope
13//! is cryptographically sound but attributable to nobody, and this handler is
14//! never constructed — see [`crate::unpack_trust_task`].
15
16use trust_tasks_rs::{TransportContext, TransportHandler};
17
18use crate::pack::Carriage;
19
20/// Stable identifier for the DIDComm v1 binding, per SPEC.md §9.2.
21pub const BINDING_URI: &str = "https://trusttasks.org/binding/didcomm-v1/0.2";
22
23/// A [`TransportHandler`] for one DIDComm v1 exchange.
24///
25/// `local` is the DID this party controls; `peer` is the connection's
26/// `theirDid` — the DID bound to the verkey that authenticated the envelope.
27/// Both are `Option<String>` to match the v2.1 handler's shape, but in practice
28/// a consumer only builds one after [`crate::unpack_trust_task`] has established
29/// an authenticated sender, so `peer` is populated on the inbound path.
30#[derive(Debug, Clone)]
31pub struct DidcommV1Handler {
32 local: Option<String>,
33 peer: Option<String>,
34 carriage: Carriage,
35}
36
37impl DidcommV1Handler {
38 /// Construct a handler. Either side may be `None`.
39 ///
40 /// The carriage defaults to [`Carriage::Dedicated`]; the consumer path in
41 /// [`crate::unpack_trust_task`] sets the one actually observed.
42 pub fn new(local: impl Into<Option<String>>, peer: impl Into<Option<String>>) -> Self {
43 Self {
44 local: local.into(),
45 peer: peer.into(),
46 carriage: Carriage::Dedicated,
47 }
48 }
49
50 /// Record which carriage the message arrived on (binding §2.3).
51 pub fn with_carriage(mut self, carriage: Carriage) -> Self {
52 self.carriage = carriage;
53 self
54 }
55
56 /// Which carriage the message arrived on.
57 ///
58 /// [`Carriage::LegacyBasicMessage`] means the peer is still emitting the
59 /// superseded `0.1` form; §2.3 SHOULDs surfacing that so an operator can
60 /// see who has not migrated.
61 pub fn carriage(&self) -> Carriage {
62 self.carriage
63 }
64
65 /// The local party's DID, if set.
66 pub fn local(&self) -> Option<&str> {
67 self.local.as_deref()
68 }
69
70 /// The connection's `theirDid`, if set.
71 pub fn peer(&self) -> Option<&str> {
72 self.peer.as_deref()
73 }
74}
75
76impl TransportHandler for DidcommV1Handler {
77 fn binding_uri(&self) -> &str {
78 BINDING_URI
79 }
80
81 fn derive_parties(&self) -> TransportContext {
82 TransportContext {
83 issuer: self.peer.clone(),
84 recipient: self.local.clone(),
85 }
86 }
87}