didcomm/protocols/routing/
mod.rs1mod forward;
2
3use std::collections::HashMap;
4
5use serde_json::{json, Value};
6use uuid::Uuid;
7
8use crate::{
9 algorithms::AnonCryptAlg,
10 did::{DIDCommMessagingService, DIDResolver, Service, ServiceKind},
11 error::{err_msg, ErrorKind, Result, ResultContext, ResultExt},
12 message::{anoncrypt, MessagingServiceMetadata},
13 utils::did::{did_or_url, is_did},
14 Attachment, AttachmentData, Message, PackEncryptedOptions,
15};
16
17pub use self::forward::ParsedForward;
18
19pub(crate) const FORWARD_MSG_TYPE: &str = "https://didcomm.org/routing/2.0/forward";
20
21pub(crate) const DIDCOMM_V2_PROFILE: &str = "didcomm/v2";
22
23async fn find_did_comm_service<'dr>(
24 did: &str,
25 service_id: Option<&str>,
26 did_resolver: &'dr (dyn DIDResolver + 'dr),
27) -> Result<Option<(String, DIDCommMessagingService)>> {
28 let did_doc = did_resolver
29 .resolve(did)
30 .await
31 .context("Unable resolve DID")?
32 .ok_or_else(|| err_msg(ErrorKind::DIDNotResolved, "DID not found"))?;
33
34 match service_id {
35 Some(service_id) => {
36 let service: &Service = did_doc
37 .service
38 .iter()
39 .find(|&service| service.id == service_id)
40 .ok_or_else(|| {
41 err_msg(
42 ErrorKind::IllegalArgument,
43 "Service with the specified ID not found",
44 )
45 })?;
46
47 match service.service_endpoint {
48 ServiceKind::DIDCommMessaging { ref value } => match value.accept.as_ref() {
49 Some(accept) => {
50 if accept.is_empty() || accept.contains(&DIDCOMM_V2_PROFILE.into()) {
51 Ok(Some((service.id.clone(), value.clone())))
52 } else {
53 Err(err_msg(
54 ErrorKind::IllegalArgument,
55 "Service with the specified ID does not accept didcomm/v2 profile",
56 ))
57 }
58 }
59 None => Ok(Some((service.id.clone(), value.clone()))),
60 },
61 _ => Err(err_msg(
62 ErrorKind::IllegalArgument,
63 "Service with the specified ID is not of DIDCommMessaging type",
64 )),
65 }
66 }
67
68 None => Ok(did_doc
69 .service
70 .iter()
71 .find_map(|service| match service.service_endpoint {
72 ServiceKind::DIDCommMessaging { ref value } => match value.accept.as_ref() {
73 Some(accept) => {
74 if accept.is_empty() || accept.contains(&DIDCOMM_V2_PROFILE.into()) {
75 Some((service.id.clone(), value.clone()))
76 } else {
77 None
78 }
79 }
80 None => Some((service.id.clone(), value.clone())),
81 },
82 _ => None,
83 })),
84 }
85}
86
87async fn resolve_did_comm_services_chain<'dr>(
88 to: &str,
89 service_id: Option<&str>,
90 did_resolver: &'dr (dyn DIDResolver + 'dr),
91) -> Result<Vec<(String, DIDCommMessagingService)>> {
92 let (to_did, _) = did_or_url(to);
93
94 let service = find_did_comm_service(to_did, service_id, did_resolver).await?;
95
96 if service.is_none() {
97 return Ok(vec![]);
98 }
99
100 let mut service = service.unwrap();
101
102 let mut services = vec![service.clone()];
103 let mut service_endpoint = &service.1.uri;
104
105 while is_did(service_endpoint) {
106 if services.len() > 1 {
109 return Err(err_msg(
110 ErrorKind::InvalidState,
111 "DID doc defines alternative endpoints recursively",
112 ));
113 }
114
115 service = find_did_comm_service(service_endpoint, None, did_resolver)
116 .await?
117 .ok_or_else(|| {
118 err_msg(
119 ErrorKind::InvalidState,
121 "Referenced mediator does not provide any DIDCommMessaging services",
122 )
123 })?;
124
125 services.insert(0, service.clone());
126 service_endpoint = &service.1.uri;
127 }
128
129 Ok(services)
130}
131
132fn generate_message_id() -> String {
133 Uuid::new_v4().to_string()
134}
135
136fn build_forward_message(
137 forwarded_msg: &str,
138 next: &str,
139 headers: Option<&HashMap<String, Value>>,
140) -> Result<String> {
141 let body = json!({ "next": next });
142
143 let attachment = Attachment::json(
147 serde_json::from_str(forwarded_msg)
148 .kind(ErrorKind::Malformed, "Unable deserialize forwarded message")?,
149 )
150 .finalize();
151
152 let mut msg_builder = Message::build(generate_message_id(), FORWARD_MSG_TYPE.to_owned(), body);
153
154 if let Some(headers) = headers {
155 for (name, value) in headers {
156 msg_builder = msg_builder.header(name.to_owned(), value.to_owned());
157 }
158 }
159
160 msg_builder = msg_builder.attachment(attachment);
161
162 let msg = msg_builder.finalize();
163
164 serde_json::to_string(&msg).kind(ErrorKind::InvalidState, "Unable serialize forward message")
165}
166
167pub fn try_parse_forward(msg: &Message) -> Option<ParsedForward> {
176 if msg.type_ != FORWARD_MSG_TYPE {
177 return None;
178 }
179
180 let next = match msg.body {
181 Value::Object(ref body) => match body.get("next") {
182 Some(&Value::String(ref next)) => Some(next),
183 _ => None,
184 },
185 _ => None,
186 };
187
188 if next.is_none() {
189 return None;
190 }
191
192 let next = next.unwrap();
193
194 let json_attachment_data = match msg.attachments {
195 Some(ref attachments) => match &attachments[..] {
196 [attachment, ..] => match &attachment.data {
197 AttachmentData::Json { ref value } => Some(value),
198 _ => None,
199 },
200 _ => None,
201 },
202 None => None,
203 };
204
205 if json_attachment_data.is_none() {
206 return None;
207 }
208
209 let forwarded_msg = &json_attachment_data.unwrap().json;
210
211 Some(ParsedForward {
212 msg,
213 next: next.clone(),
214 forwarded_msg: forwarded_msg.clone(),
215 })
216}
217
218pub async fn wrap_in_forward<'dr>(
241 msg: &str,
242 headers: Option<&HashMap<String, Value>>,
243 to: &str,
244 routing_keys: &Vec<String>,
245 enc_alg_anon: &AnonCryptAlg,
246 did_resolver: &'dr (dyn DIDResolver + 'dr),
247) -> Result<String> {
248 let mut tos = routing_keys.clone();
249
250 let mut nexts = tos.clone();
251 nexts.remove(0);
252 nexts.push(to.to_owned());
253
254 tos.reverse();
255 nexts.reverse();
256
257 let mut msg = msg.to_owned();
258
259 for (to_, next_) in tos.iter().zip(nexts.iter()) {
260 msg = build_forward_message(&msg, next_, headers)?;
261 msg = anoncrypt(to_, did_resolver, msg.as_bytes(), enc_alg_anon)
262 .await?
263 .0;
264 }
265
266 Ok(msg)
267}
268
269pub(crate) async fn wrap_in_forward_if_needed<'dr>(
270 msg: &str,
271 to: &str,
272 did_resolver: &'dr (dyn DIDResolver + 'dr),
273 options: &PackEncryptedOptions,
274) -> Result<Option<(String, MessagingServiceMetadata)>> {
275 if !options.forward {
276 return Ok(None);
277 }
278
279 let services_chain =
280 resolve_did_comm_services_chain(to, options.messaging_service.as_deref(), did_resolver)
281 .await?;
282
283 if services_chain.is_empty() {
284 return Ok(None);
285 }
286
287 let mut routing_keys = services_chain[1..]
288 .iter()
289 .map(|service| service.1.uri.clone())
290 .collect::<Vec<_>>();
291
292 routing_keys.append(&mut services_chain.last().unwrap().1.routing_keys.clone());
293
294 if routing_keys.is_empty() {
295 return Ok(None);
296 }
297
298 let forward_msg = wrap_in_forward(
299 msg,
300 options.forward_headers.as_ref(),
301 to,
302 &routing_keys,
303 &options.enc_alg_anon,
304 did_resolver,
305 )
306 .await?;
307
308 let messaging_service = MessagingServiceMetadata {
309 id: services_chain.last().unwrap().0.clone(),
310 service_endpoint: services_chain.first().unwrap().1.uri.clone(),
311 };
312
313 Ok(Some((forward_msg, messaging_service)))
314}