tuwunel_api/client/keys/
upload_keys.rs1use axum::extract::State;
2use ruma::{
3 DeviceId, UserId, api::client::keys::upload_keys, encryption::DeviceKeys, serde::Raw,
4};
5use tuwunel_core::{Err, Result, debug, err};
6use tuwunel_service::Services;
7
8use crate::Ruma;
9
10pub(crate) async fn upload_keys_route(
18 State(services): State<crate::State>,
19 body: Ruma<upload_keys::v3::Request>,
20) -> Result<upload_keys::v3::Response> {
21 let sender_user = body.sender_user();
22 let sender_device = body.sender_device()?;
23
24 let one_time_keys = body
25 .one_time_keys
26 .iter()
27 .take(services.config.one_time_key_limit)
28 .map(|(id, val)| (id.as_ref(), val));
29
30 services
31 .users
32 .add_one_time_keys(sender_user, sender_device, one_time_keys)
33 .await?;
34
35 let fallback_keys = body
36 .fallback_keys
37 .iter()
38 .map(|(id, val)| (id.as_ref(), val));
39
40 services
41 .users
42 .add_fallback_keys(sender_user, sender_device, fallback_keys)
43 .await?;
44
45 if let Some(device_keys) = body.device_keys.as_ref() {
46 store_device_keys(&services, sender_user, sender_device, device_keys).await?;
47 }
48
49 Ok(upload_keys::v3::Response {
50 one_time_key_counts: services
51 .users
52 .count_one_time_keys(sender_user, sender_device)
53 .await,
54 })
55}
56
57async fn store_device_keys(
58 services: &Services,
59 sender_user: &UserId,
60 sender_device: &DeviceId,
61 device_keys: &Raw<DeviceKeys>,
62) -> Result {
63 let new_keys = device_keys.deserialize().map_err(|e| {
64 err!(Request(BadJson(debug_warn!(
65 ?device_keys,
66 "Invalid device keys JSON uploaded by client: {e}"
67 ))))
68 })?;
69
70 if new_keys.user_id != sender_user {
71 return Err!(Request(Unknown(
72 "User ID in keys uploaded does not match your own user ID"
73 )));
74 }
75 if new_keys.device_id != sender_device {
76 return Err!(Request(Unknown(
77 "Device ID in keys uploaded does not match your own device ID"
78 )));
79 }
80
81 let unchanged = services
85 .users
86 .get_device_keys(sender_user, sender_device)
87 .await
88 .and_then(|keys| keys.deserialize().map_err(Into::into))
89 .is_ok_and(|existing| existing.keys == new_keys.keys);
90
91 if unchanged {
92 debug!(
93 ?sender_user,
94 ?sender_device,
95 ?device_keys,
96 "Ignoring user uploaded keys as they are an exact copy already in the database"
97 );
98
99 return Ok(());
100 }
101
102 services
103 .users
104 .add_device_keys(sender_user, sender_device, device_keys)
105 .await;
106
107 Ok(())
108}