1use std::collections::{BTreeMap, HashMap};
2
3use axum::extract::State;
4use futures::{
5 FutureExt, StreamExt,
6 future::{join, join4},
7};
8use ruma::{
9 CanonicalJsonObject, CanonicalJsonValue, DeviceId, OwnedDeviceId, OwnedUserId, ServerName,
10 UserId,
11 api::{
12 client::{device::Device, keys::get_keys},
13 federation::keys::get_keys::v1::{
14 Request as FederationRequest, Response as FederationResponse,
15 },
16 },
17 encryption::{CrossSigningKey, DeviceKeys},
18 serde::Raw,
19};
20use serde_json::value::to_raw_value;
21use tuwunel_core::{
22 Result, debug_warn, implement,
23 utils::{
24 BoolExt, IterStream,
25 future::TryExtExt,
26 json,
27 stream::{BroadbandExt, ReadyExt},
28 },
29 warn,
30};
31use tuwunel_service::{
32 Services,
33 federation::feds::{Outcome, OutcomeExt, fanout_with},
34 users::parse_master_key,
35};
36
37use super::{FailureMap, federation_failures, federation_opts};
38use crate::Ruma;
39
40#[derive(Default)]
41struct Keys {
42 device_keys: DeviceKeyMap,
43 master_keys: CrossSigningKeys,
44 self_signing_keys: CrossSigningKeys,
45 user_signing_keys: CrossSigningKeys,
46 failures: FailureMap,
47}
48
49type DeviceLists = BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>;
50type DeviceKeyMap = BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Raw<DeviceKeys>>>;
51type ServerDevices<'a> = HashMap<&'a ServerName, DeviceLists>;
52type LocalDeviceUser<'a> = (&'a UserId, &'a Vec<OwnedDeviceId>);
53type CrossSigningKeys = BTreeMap<OwnedUserId, Raw<CrossSigningKey>>;
54
55pub(crate) async fn get_keys_route(
64 State(services): State<crate::State>,
65 body: Ruma<get_keys::v3::Request>,
66) -> Result<get_keys::v3::Response> {
67 let sender_user = body.sender_user();
68
69 get_keys_helper(
70 &services,
71 Some(sender_user),
72 &body.device_keys,
73 |u| u == sender_user,
74 true, )
76 .await
77}
78
79pub async fn get_keys_helper<F>(
84 services: &Services,
85 sender_user: Option<&UserId>,
86 device_keys_input: &DeviceLists,
87 allowed_signatures: F,
88 include_display_names: bool,
89) -> Result<get_keys::v3::Response>
90where
91 F: Fn(&UserId) -> bool + Send + Sync,
92{
93 let (local_users, remote_users): (Vec<LocalDeviceUser<'_>>, Vec<_>) = device_keys_input
94 .iter()
95 .map(|(uid, dids)| (uid.as_ref(), dids))
96 .partition(|(user_id, _)| services.globals.user_is_local(user_id));
97
98 let server: ServerDevices<'_> =
99 remote_users
100 .into_iter()
101 .fold(HashMap::new(), |mut acc, (user_id, device_ids)| {
102 acc.entry(user_id.server_name())
103 .or_default()
104 .insert(user_id.to_owned(), device_ids.clone());
105 acc
106 });
107
108 let local = collect_local_keys(
109 services,
110 &local_users,
111 sender_user,
112 &allowed_signatures,
113 include_display_names,
114 );
115
116 let federation = collect_federation_keys(services, server, sender_user, &allowed_signatures);
117
118 let (local, federation) = join(local, federation).await;
119 Ok(local.merge(federation).into_response())
120}
121
122async fn collect_local_keys<F>(
123 services: &Services,
124 users: &[LocalDeviceUser<'_>],
125 sender_user: Option<&UserId>,
126 allowed_signatures: &F,
127 include_display_names: bool,
128) -> Keys
129where
130 F: Fn(&UserId) -> bool + Send + Sync,
131{
132 users
133 .iter()
134 .copied()
135 .stream()
136 .broad_then(async |(user_id, device_ids)| {
137 collect_local_user_keys(
138 services,
139 user_id,
140 device_ids,
141 sender_user,
142 allowed_signatures,
143 include_display_names,
144 )
145 .await
146 })
147 .ready_fold(Keys::default(), Keys::merge)
148 .await
149}
150
151async fn collect_local_user_keys<F>(
152 services: &Services,
153 user_id: &UserId,
154 device_ids: &[OwnedDeviceId],
155 sender_user: Option<&UserId>,
156 allowed_signatures: &F,
157 include_display_names: bool,
158) -> Keys
159where
160 F: Fn(&UserId) -> bool + Send + Sync,
161{
162 let device_keys =
163 collect_local_device_keys(services, user_id, device_ids, include_display_names);
164
165 let master_key = services
166 .users
167 .get_master_key(sender_user, user_id, allowed_signatures)
168 .ok();
169
170 let self_signing_key = services
171 .users
172 .get_self_signing_key(sender_user, user_id, allowed_signatures)
173 .ok();
174
175 let user_signing_key = (Some(user_id) == sender_user)
176 .then_async(|| services.users.get_user_signing_key(user_id).ok())
177 .map(Option::flatten);
178
179 let (device_keys, master_key, self_signing_key, user_signing_key) =
180 join4(device_keys, master_key, self_signing_key, user_signing_key).await;
181
182 let owned = || user_id.to_owned();
183 Keys {
184 device_keys: BTreeMap::from([(owned(), device_keys)]),
185 master_keys: master_key
186 .map(|k| (owned(), k))
187 .into_iter()
188 .collect(),
189
190 self_signing_keys: self_signing_key
191 .map(|k| (owned(), k))
192 .into_iter()
193 .collect(),
194
195 user_signing_keys: user_signing_key
196 .map(|k| (owned(), k))
197 .into_iter()
198 .collect(),
199
200 ..Default::default()
201 }
202}
203
204async fn collect_local_device_keys(
205 services: &Services,
206 user_id: &UserId,
207 device_ids: &[OwnedDeviceId],
208 include_display_names: bool,
209) -> BTreeMap<OwnedDeviceId, Raw<DeviceKeys>> {
210 let stream = if device_ids.is_empty() {
211 services
212 .users
213 .all_device_ids(user_id)
214 .map(ToOwned::to_owned)
215 .left_stream()
216 } else {
217 device_ids.iter().cloned().stream().right_stream()
218 };
219
220 stream
221 .broad_filter_map(async |device_id| {
222 get_local_device_keys(services, user_id, &device_id, include_display_names)
223 .await
224 .map(|keys| (device_id, keys))
225 })
226 .collect()
227 .await
228}
229
230async fn get_local_device_keys(
231 services: &Services,
232 user_id: &UserId,
233 device_id: &DeviceId,
234 include_display_names: bool,
235) -> Option<Raw<DeviceKeys>> {
236 let mut keys = services
237 .users
238 .get_device_keys(user_id, device_id)
239 .await
240 .ok()?;
241
242 let metadata = services
243 .users
244 .get_device_metadata(user_id, device_id)
245 .await
246 .inspect_err(|e| debug_warn!(?user_id, ?device_id, "device metadata missing: {e}"))
247 .ok()?;
248
249 add_unsigned_device_display_name(&mut keys, metadata, include_display_names)
250 .inspect_err(|e| debug_warn!(?user_id, ?device_id, "invalid device keys: {e}"))
251 .ok()?;
252
253 Some(keys)
254}
255
256async fn collect_federation_keys<F>(
257 services: &Services,
258 server: ServerDevices<'_>,
259 sender_user: Option<&UserId>,
260 allowed_signatures: &F,
261) -> Keys
262where
263 F: Fn(&UserId) -> bool + Send + Sync,
264{
265 let requests = server
266 .into_iter()
267 .stream()
268 .map(|(server, device_keys)| (server.to_owned(), FederationRequest { device_keys }));
269
270 let outcomes = fanout_with(
271 requests,
272 async |server, request| {
273 services
274 .federation
275 .execute_keys(&server, request)
276 .await
277 },
278 federation_opts(services),
279 )
280 .broad_then(async |outcome| {
281 process_federation_outcome(services, sender_user, allowed_signatures, outcome).await
282 });
283
284 let (keys, faults) = outcomes.merge(Keys::default(), Keys::merge).await;
285
286 Keys {
287 failures: federation_failures("get_keys", faults).collect(),
288 ..keys
289 }
290}
291
292async fn process_federation_outcome<F>(
293 services: &Services,
294 sender_user: Option<&UserId>,
295 allowed_signatures: &F,
296 outcome: Outcome<FederationResponse>,
297) -> Outcome<Keys>
298where
299 F: Fn(&UserId) -> bool + Send + Sync,
300{
301 let Outcome { origin, elapsed, result } = outcome;
302 let result = match result {
303 | Err(fault) => Err(fault),
304 | Ok(response) => {
305 let response = retain_origin_users(&origin, response);
306 let keys =
307 process_federation_response(services, sender_user, allowed_signatures, response)
308 .await;
309
310 Ok(keys)
311 },
312 };
313
314 Outcome { origin, elapsed, result }
315}
316
317fn retain_origin_users(
322 origin: &ServerName,
323 mut response: FederationResponse,
324) -> FederationResponse {
325 response
326 .device_keys
327 .retain(|user, _| user.server_name().eq(origin));
328
329 response
330 .master_keys
331 .retain(|user, _| user.server_name().eq(origin));
332
333 response
334 .self_signing_keys
335 .retain(|user, _| user.server_name().eq(origin));
336
337 response
338}
339
340async fn process_federation_response<F>(
341 services: &Services,
342 sender_user: Option<&UserId>,
343 allowed_signatures: &F,
344 response: FederationResponse,
345) -> Keys
346where
347 F: Fn(&UserId) -> bool + Send + Sync,
348{
349 let FederationResponse {
350 master_keys,
351 self_signing_keys,
352 device_keys,
353 } = response;
354
355 let master_keys = master_keys
356 .into_iter()
357 .stream()
358 .broad_filter_map(async |(user, master_key)| {
359 merge_remote_master_key(services, sender_user, allowed_signatures, &user, master_key)
360 .await
361 .inspect_err(|e| debug_warn!(?user, "skipping master key from federation: {e}"))
362 .map(|raw| (user, raw))
363 .ok()
364 })
365 .collect()
366 .await;
367
368 Keys {
369 device_keys,
370 master_keys,
371 self_signing_keys,
372 user_signing_keys: BTreeMap::new(),
373 failures: BTreeMap::new(),
374 }
375}
376
377async fn merge_remote_master_key<F>(
383 services: &Services,
384 sender_user: Option<&UserId>,
385 allowed_signatures: &F,
386 user: &UserId,
387 master_key_raw: Raw<CrossSigningKey>,
388) -> Result<Raw<CrossSigningKey>>
389where
390 F: Fn(&UserId) -> bool + Send + Sync,
391{
392 let (row_key, master_key) = parse_master_key(user, &master_key_raw)?;
393 let our_raw = services
394 .users
395 .get_key(&row_key, sender_user, user, allowed_signatures)
396 .await;
397
398 let master_key = merge_signatures(user, master_key, our_raw);
399 let raw = json::to_raw(&master_key)?;
400
401 services
402 .users
403 .store_remote_master_key(user, &master_key_raw)
404 .await
405 .inspect_err(|error| warn!(%user, %error, "Not storing a master key from federation."))
406 .ok();
407
408 Ok(raw)
409}
410
411fn merge_signatures(
412 user: &UserId,
413 mut master_key: CrossSigningKey,
414 our_raw: Result<Raw<CrossSigningKey>>,
415) -> CrossSigningKey {
416 if let Ok(our_raw) = our_raw
417 && let Ok((_, mut ours)) = parse_master_key(user, &our_raw)
418 {
419 master_key.signatures.append(&mut ours.signatures);
420 }
421
422 master_key
423}
424
425fn add_unsigned_device_display_name(
426 keys: &mut Raw<DeviceKeys>,
427 metadata: Device,
428 include_display_names: bool,
429) -> Result {
430 let Some(display_name) = metadata.display_name else {
431 return Ok(());
432 };
433
434 let mut object = keys.deserialize_as_unchecked::<CanonicalJsonObject>()?;
435
436 if let CanonicalJsonValue::Object(unsigned) = object
437 .entry("unsigned".into())
438 .or_insert_with(|| CanonicalJsonObject::default().into())
439 {
440 let display_name = if include_display_names {
441 CanonicalJsonValue::String(display_name.to_string())
442 } else {
443 CanonicalJsonValue::String(metadata.device_id.into())
444 };
445
446 unsigned.insert("device_display_name".into(), display_name);
447 }
448
449 *keys = Raw::from_json(to_raw_value(&object)?);
450
451 Ok(())
452}
453
454#[implement(Keys)]
455fn merge(mut self, other: Self) -> Self {
456 self.failures.extend(other.failures);
457 self.device_keys.extend(other.device_keys);
458 self.master_keys.extend(other.master_keys);
459 self.self_signing_keys
460 .extend(other.self_signing_keys);
461 self.user_signing_keys
462 .extend(other.user_signing_keys);
463 self
464}
465
466#[implement(Keys)]
467fn into_response(self) -> get_keys::v3::Response {
468 get_keys::v3::Response {
469 failures: self.failures,
470 device_keys: self.device_keys,
471 master_keys: self.master_keys,
472 self_signing_keys: self.self_signing_keys,
473 user_signing_keys: self.user_signing_keys,
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use ruma::{server_name, user_id};
480 use serde_json::json;
481
482 use super::*;
483
484 #[test]
485 fn drops_keys_of_users_on_other_servers() {
486 let remote = user_id!("@bob:remote.example");
487 let users = [remote, user_id!("@alice:local.example"), user_id!("@carol:third.example")];
488 let devices = |user: &UserId| (user.to_owned(), BTreeMap::new());
489 let key = |user: &UserId| (user.to_owned(), Raw::from_json_value(&json!({})));
490 let response = FederationResponse {
491 device_keys: users.map(devices).into(),
492 master_keys: users.map(key).into(),
493 self_signing_keys: users.map(key).into(),
494 };
495
496 let response = retain_origin_users(server_name!("remote.example"), response);
497
498 assert!(response.device_keys.keys().eq([remote]));
499 assert!(response.master_keys.keys().eq([remote]));
500 assert!(response.self_signing_keys.keys().eq([remote]));
501 }
502}