tuwunel_api/client/keys/
upload_signing_keys.rs1use axum::extract::State;
2use ruma::{
3 UserId,
4 api::client::{
5 keys::upload_signing_keys,
6 uiaa::{AuthFlow, AuthType, UiaaInfo},
7 },
8 encryption::CrossSigningKey,
9 serde::Raw,
10};
11use serde_json::{json, value::to_raw_value};
12use tuwunel_core::{
13 Err, Error, Result, debug, debug_error, err,
14 result::NotFound,
15 utils,
16 utils::{BoolExt, OptionExt},
17};
18use tuwunel_service::{Services, uiaa::SESSION_ID_LENGTH, users::parse_master_key};
19
20use crate::{Ruma, router::auth_uiaa};
21
22pub(crate) async fn upload_signing_keys_route(
31 State(services): State<crate::State>,
32 body: Ruma<upload_signing_keys::v3::Request>,
33) -> Result<upload_signing_keys::v3::Response> {
34 let sender_user = body.sender_user();
35
36 if let Ok(exists) = check_for_new_keys(
39 &services,
40 sender_user,
41 body.self_signing_key.as_ref(),
42 body.user_signing_key.as_ref(),
43 body.master_key.as_ref(),
44 )
45 .await
46 .inspect_err(|e| debug_error!(?e))
47 {
48 if let Some(result) = exists {
49 return Ok(result);
52 }
53
54 debug!("Skipping UIA as per MSC3967: user had no existing keys");
56 return persist_signing_keys(&services, &body).await;
57 }
58
59 if body
62 .appservice_info
63 .as_ref()
64 .is_some_and(|appservice| appservice.registration.device_management)
65 {
66 debug!(
67 "Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
68 enabled"
69 );
70
71 return persist_signing_keys(&services, &body).await;
72 }
73
74 let is_oidc = body
75 .sender_device()
76 .ok()
77 .map_async(|sender_device| {
78 services
79 .users
80 .is_oidc_device(sender_user, sender_device)
81 })
82 .await
83 .unwrap_or(false);
84
85 if is_oidc
88 && services
89 .users
90 .can_replace_cross_signing_keys(sender_user)
91 .await
92 {
93 return persist_signing_keys(&services, &body).await;
94 }
95
96 if is_oidc && body.auth.is_none() {
98 return Err(Error::Uiaa(create_oauth_uiaa(&services, sender_user, &body)?));
99 }
100
101 let authed_user = auth_uiaa(&services, &body).await?;
102
103 assert_eq!(sender_user, authed_user, "Expected UIAA of {sender_user} and not {authed_user}");
104 persist_signing_keys(&services, &body).await
105}
106
107async fn persist_signing_keys(
108 services: &Services,
109 body: &Ruma<upload_signing_keys::v3::Request>,
110) -> Result<upload_signing_keys::v3::Response> {
111 services
112 .users
113 .add_cross_signing_keys(
114 body.sender_user(),
115 &body.master_key,
116 &body.self_signing_key,
117 &body.user_signing_key,
118 true, )
120 .await?;
121
122 Ok(upload_signing_keys::v3::Response {})
123}
124
125fn create_oauth_uiaa(
126 services: &Services,
127 sender_user: &UserId,
128 body: &Ruma<upload_signing_keys::v3::Request>,
129) -> Result<UiaaInfo> {
130 let session = utils::random_string(SESSION_ID_LENGTH);
131 let issuer = services.oauth.get_server()?.issuer_url()?;
132 let base = issuer.trim_end_matches('/');
133 let url = format!("{base}/_tuwunel/oidc/account?action=org.matrix.cross_signing_reset");
134
135 let uiaainfo = UiaaInfo {
136 flows: vec![AuthFlow { stages: vec![AuthType::OAuth] }],
137 params: Some(to_raw_value(&json!({"m.oauth": { "url": url }}))?),
138 session: Some(session),
139 ..Default::default()
140 };
141
142 services.uiaa.create(
143 sender_user,
144 body.sender_device()?,
145 &uiaainfo,
146 body.json_body
147 .as_ref()
148 .ok_or_else(|| err!(Request(NotJson("JSON body is not valid"))))?,
149 );
150
151 Ok(uiaainfo)
152}
153
154async fn check_for_new_keys(
155 services: &Services,
156 user_id: &UserId,
157 self_signing_key: Option<&Raw<CrossSigningKey>>,
158 user_signing_key: Option<&Raw<CrossSigningKey>>,
159 master_signing_key: Option<&Raw<CrossSigningKey>>,
160) -> Result<Option<upload_signing_keys::v3::Response>> {
161 debug!("checking for existing keys");
162
163 let empty = match master_signing_key {
164 | Some(new_master) => !master_key_matches(services, user_id, new_master).await?,
165 | None => false,
166 };
167
168 if let Some(new_user_signing) = user_signing_key {
169 let fetched = services.users.get_user_signing_key(user_id).await;
170
171 if fetched.is_not_found() {
172 if !empty {
173 return Err!(Request(Forbidden(
174 "Tried to update an existing user signing key, UIA required"
175 )));
176 }
177 } else if fetched?.deserialize()? != new_user_signing.deserialize()? {
178 return Err!(Request(Forbidden(
179 "Tried to change an existing user signing key, UIA required"
180 )));
181 }
182 }
183
184 if let Some(new_self_signing) = self_signing_key {
185 let fetched = services
186 .users
187 .get_self_signing_key(None, user_id, &|_| true)
188 .await;
189
190 if fetched.is_not_found() {
191 if !empty {
192 return Err!(Request(Forbidden(
193 "Tried to add a new signing key independently from the master key"
194 )));
195 }
196 } else if fetched?.deserialize()? != new_self_signing.deserialize()? {
197 return Err!(Request(Forbidden(
198 "Tried to update an existing self signing key, UIA required"
199 )));
200 }
201 }
202
203 Ok(empty
204 .is_false()
205 .into_option()
206 .map(|()| upload_signing_keys::v3::Response {}))
207}
208
209async fn master_key_matches(
213 services: &Services,
214 user_id: &UserId,
215 new_master: &Raw<CrossSigningKey>,
216) -> Result<bool> {
217 let (new_id, new_value) = parse_master_key(user_id, new_master)?;
218 let existing = services
219 .users
220 .get_master_key(None, user_id, &|_| true)
221 .await;
222
223 if existing.is_not_found() {
224 return Ok(false);
225 }
226
227 let (existing_id, existing_value) = parse_master_key(user_id, &existing?)?;
228 if existing_id != new_id || existing_value != new_value {
229 return Err!(Request(Forbidden("Tried to change an existing master key, UIA required")));
230 }
231
232 Ok(true)
233}