1use axum::extract::State;
2use ruma::{
3 UserId,
4 api::client::{
5 keys::upload_signing_keys,
6 uiaa::{AuthFlow, AuthType, UiaaInfo},
7 },
8 encryption::{CrossSigningKey, KeyUsage},
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
22struct Keys<'a> {
23 user_id: &'a UserId,
24 master_key: &'a Option<Raw<CrossSigningKey>>,
25 self_signing_key: &'a Option<Raw<CrossSigningKey>>,
26 user_signing_key: &'a Option<Raw<CrossSigningKey>>,
27}
28
29struct ValidatedKeys<'a>(Keys<'a>);
30
31pub(crate) async fn upload_signing_keys_route(
40 State(services): State<crate::State>,
41 body: Ruma<upload_signing_keys::v3::Request>,
42) -> Result<upload_signing_keys::v3::Response> {
43 let sender_user = body.sender_user();
44
45 let keys = validate_keys(Keys {
46 user_id: sender_user,
47 master_key: &body.master_key,
48 self_signing_key: &body.self_signing_key,
49 user_signing_key: &body.user_signing_key,
50 })?;
51
52 if let Ok(exists) = check_for_new_keys(
55 &services,
56 sender_user,
57 body.self_signing_key.as_ref(),
58 body.user_signing_key.as_ref(),
59 body.master_key.as_ref(),
60 )
61 .await
62 .inspect_err(|e| debug_error!(?e))
63 {
64 if let Some(result) = exists {
65 return Ok(result);
68 }
69
70 debug!("Skipping UIA as per MSC3967: user had no existing keys");
72 return persist_signing_keys(&services, keys).await;
73 }
74
75 if body
78 .appservice_info
79 .as_ref()
80 .is_some_and(|appservice| appservice.registration.device_management)
81 {
82 debug!(
83 "Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
84 enabled"
85 );
86
87 return persist_signing_keys(&services, keys).await;
88 }
89
90 let is_oidc = body
91 .sender_device()
92 .ok()
93 .map_async(|sender_device| {
94 services
95 .users
96 .is_oidc_device(sender_user, sender_device)
97 })
98 .await
99 .unwrap_or(false);
100
101 if is_oidc
104 && services
105 .users
106 .can_replace_cross_signing_keys(sender_user)
107 .await
108 {
109 return persist_signing_keys(&services, keys).await;
110 }
111
112 if is_oidc && body.auth.is_none() {
114 return Err(Error::Uiaa(create_oauth_uiaa(&services, sender_user, &body)?));
115 }
116
117 let authed_user = auth_uiaa(&services, &body).await?;
118
119 assert_eq!(sender_user, authed_user, "Expected UIAA of {sender_user} and not {authed_user}");
120 persist_signing_keys(&services, keys).await
121}
122
123fn validate_keys(keys: Keys<'_>) -> Result<ValidatedKeys<'_>> {
124 [
125 (keys.master_key.as_ref(), KeyUsage::Master),
126 (keys.self_signing_key.as_ref(), KeyUsage::SelfSigning),
127 (keys.user_signing_key.as_ref(), KeyUsage::UserSigning),
128 ]
129 .into_iter()
130 .try_for_each(|(key, usage)| validate_key(keys.user_id, key, &usage))?;
131
132 Ok(ValidatedKeys(keys))
133}
134
135fn validate_key(
136 user_id: &UserId,
137 key: Option<&Raw<CrossSigningKey>>,
138 usage: &KeyUsage,
139) -> Result {
140 let Some(key) = key else {
141 return Ok(());
142 };
143
144 let key = key
145 .deserialize()
146 .map_err(|error| err!(Request(InvalidParam("Invalid cross-signing key: {error}"))))?;
147
148 if key.user_id != user_id {
149 return Err!(Request(InvalidParam("Cross-signing key belongs to another user.")));
150 }
151
152 if !key.usage.contains(usage) {
153 return Err!(Request(InvalidParam(
154 "Cross-signing key does not include the required usage."
155 )));
156 }
157
158 if key.keys.len() != 1 {
159 return Err!(Request(InvalidParam("Cross-signing key must contain exactly one key.")));
160 }
161
162 Ok(())
163}
164
165async fn persist_signing_keys(
166 services: &Services,
167 keys: ValidatedKeys<'_>,
168) -> Result<upload_signing_keys::v3::Response> {
169 let ValidatedKeys(keys) = keys;
170
171 services
172 .users
173 .add_cross_signing_keys(
174 keys.user_id,
175 keys.master_key,
176 keys.self_signing_key,
177 keys.user_signing_key,
178 true, )
180 .await?;
181
182 Ok(upload_signing_keys::v3::Response {})
183}
184
185fn create_oauth_uiaa(
186 services: &Services,
187 sender_user: &UserId,
188 body: &Ruma<upload_signing_keys::v3::Request>,
189) -> Result<UiaaInfo> {
190 let session = utils::random_string(SESSION_ID_LENGTH);
191 let issuer = services.oauth.get_server()?.issuer_url()?;
192 let base = issuer.trim_end_matches('/');
193 let url = format!("{base}/_tuwunel/oidc/account?action=org.matrix.cross_signing_reset");
194
195 let uiaainfo = UiaaInfo {
196 flows: vec![AuthFlow { stages: vec![AuthType::OAuth] }],
197 params: Some(to_raw_value(&json!({"m.oauth": { "url": url }}))?),
198 session: Some(session),
199 ..Default::default()
200 };
201
202 services.uiaa.create(
203 sender_user,
204 body.sender_device()?,
205 &uiaainfo,
206 body.json_body
207 .as_ref()
208 .ok_or_else(|| err!(Request(NotJson("JSON body is not valid"))))?,
209 );
210
211 Ok(uiaainfo)
212}
213
214async fn check_for_new_keys(
215 services: &Services,
216 user_id: &UserId,
217 self_signing_key: Option<&Raw<CrossSigningKey>>,
218 user_signing_key: Option<&Raw<CrossSigningKey>>,
219 master_signing_key: Option<&Raw<CrossSigningKey>>,
220) -> Result<Option<upload_signing_keys::v3::Response>> {
221 debug!("checking for existing keys");
222
223 let empty = match master_signing_key {
224 | Some(new_master) => !master_key_matches(services, user_id, new_master).await?,
225 | None => false,
226 };
227
228 if let Some(new_user_signing) = user_signing_key {
229 let fetched = services.users.get_user_signing_key(user_id).await;
230
231 if fetched.is_not_found() {
232 if !empty {
233 return Err!(Request(Forbidden(
234 "Tried to update an existing user signing key, UIA required"
235 )));
236 }
237 } else if fetched?.deserialize()? != new_user_signing.deserialize()? {
238 return Err!(Request(Forbidden(
239 "Tried to change an existing user signing key, UIA required"
240 )));
241 }
242 }
243
244 if let Some(new_self_signing) = self_signing_key {
245 let fetched = services
246 .users
247 .get_self_signing_key(None, user_id, &|_| true)
248 .await;
249
250 if fetched.is_not_found() {
251 if !empty {
252 return Err!(Request(Forbidden(
253 "Tried to add a new signing key independently from the master key"
254 )));
255 }
256 } else if fetched?.deserialize()? != new_self_signing.deserialize()? {
257 return Err!(Request(Forbidden(
258 "Tried to update an existing self signing key, UIA required"
259 )));
260 }
261 }
262
263 Ok(empty
264 .is_false()
265 .into_option()
266 .map(|()| upload_signing_keys::v3::Response {}))
267}
268
269async fn master_key_matches(
273 services: &Services,
274 user_id: &UserId,
275 new_master: &Raw<CrossSigningKey>,
276) -> Result<bool> {
277 let (new_id, new_value) = parse_master_key(user_id, new_master)?;
278 let existing = services
279 .users
280 .get_master_key(None, user_id, &|_| true)
281 .await;
282
283 if existing.is_not_found() {
284 return Ok(false);
285 }
286
287 let (existing_id, existing_value) = parse_master_key(user_id, &existing?)?;
288 if existing_id != new_id || existing_value != new_value {
289 return Err!(Request(Forbidden("Tried to change an existing master key, UIA required")));
290 }
291
292 Ok(true)
293}
294
295#[cfg(test)]
296mod tests {
297 use ruma::{api::error::ErrorKind::InvalidParam, user_id};
298 use serde_json::Map;
299
300 use super::*;
301
302 #[test]
303 fn accepts_valid_unsigned_keys_and_additional_usages() {
304 let user_id = user_id!("@alice:example.com");
305 let master_key =
306 Some(signing_key(user_id, &["master", "self_signing"], &["ed25519:master"]));
307
308 let self_signing_key =
309 Some(signing_key(user_id, &["self_signing", "user_signing"], &["ed25519:self"]));
310
311 let user_signing_key =
312 Some(signing_key(user_id, &["user_signing", "master"], &["ed25519:user"]));
313
314 validate_keys(Keys {
315 user_id,
316 master_key: &master_key,
317 self_signing_key: &self_signing_key,
318 user_signing_key: &user_signing_key,
319 })
320 .expect("valid unsigned keys should pass structural validation");
321 }
322
323 #[test]
324 fn rejects_wrong_owner_for_each_role() {
325 let user_id = user_id!("@alice:example.com");
326 let other_user = user_id!("@mallory:elsewhere.example");
327 let none = None;
328 let master_key = Some(signing_key(other_user, &["master"], &["ed25519:master"]));
329 let self_signing_key =
330 Some(signing_key(other_user, &["self_signing"], &["ed25519:self"]));
331
332 let user_signing_key =
333 Some(signing_key(other_user, &["user_signing"], &["ed25519:user"]));
334
335 assert_invalid(&validate_keys(Keys {
336 user_id,
337 master_key: &master_key,
338 self_signing_key: &none,
339 user_signing_key: &none,
340 }));
341
342 assert_invalid(&validate_keys(Keys {
343 user_id,
344 master_key: &none,
345 self_signing_key: &self_signing_key,
346 user_signing_key: &none,
347 }));
348
349 assert_invalid(&validate_keys(Keys {
350 user_id,
351 master_key: &none,
352 self_signing_key: &none,
353 user_signing_key: &user_signing_key,
354 }));
355 }
356
357 #[test]
358 fn rejects_missing_usage_for_each_role() {
359 let user_id = user_id!("@alice:example.com");
360 let none = None;
361 let master_key = Some(signing_key(user_id, &["self_signing"], &["ed25519:master"]));
362 let self_signing_key = Some(signing_key(user_id, &["master"], &["ed25519:self"]));
363 let user_signing_key = Some(signing_key(user_id, &["master"], &["ed25519:user"]));
364
365 assert_invalid(&validate_keys(Keys {
366 user_id,
367 master_key: &master_key,
368 self_signing_key: &none,
369 user_signing_key: &none,
370 }));
371
372 assert_invalid(&validate_keys(Keys {
373 user_id,
374 master_key: &none,
375 self_signing_key: &self_signing_key,
376 user_signing_key: &none,
377 }));
378
379 assert_invalid(&validate_keys(Keys {
380 user_id,
381 master_key: &none,
382 self_signing_key: &none,
383 user_signing_key: &user_signing_key,
384 }));
385 }
386
387 #[test]
388 fn rejects_invalid_key_counts() {
389 let user_id = user_id!("@alice:example.com");
390 let none = None;
391 let empty = Some(signing_key(user_id, &["master"], &[]));
392 let multiple =
393 Some(signing_key(user_id, &["self_signing"], &["ed25519:first", "ed25519:second"]));
394
395 assert_invalid(&validate_keys(Keys {
396 user_id,
397 master_key: &empty,
398 self_signing_key: &none,
399 user_signing_key: &none,
400 }));
401
402 assert_invalid(&validate_keys(Keys {
403 user_id,
404 master_key: &none,
405 self_signing_key: &multiple,
406 user_signing_key: &none,
407 }));
408 }
409
410 #[test]
411 fn rejects_a_malformed_later_key_before_persistence() {
412 let user_id = user_id!("@alice:example.com");
413 let master_key = Some(signing_key(user_id, &["master"], &["ed25519:master"]));
414 let self_signing_key = Some(signing_key(user_id, &["self_signing"], &["ed25519:self"]));
415 let user_signing_key = Some(signing_key(user_id, &["master"], &["ed25519:user"]));
416
417 assert_invalid(&validate_keys(Keys {
418 user_id,
419 master_key: &master_key,
420 self_signing_key: &self_signing_key,
421 user_signing_key: &user_signing_key,
422 }));
423 }
424
425 fn signing_key(user_id: &UserId, usage: &[&str], key_ids: &[&str]) -> Raw<CrossSigningKey> {
426 let keys: Map<_, _> = key_ids
427 .iter()
428 .map(|key_id| ((*key_id).to_owned(), json!("public-key")))
429 .collect();
430
431 let key = json!({
432 "user_id": user_id,
433 "usage": usage,
434 "keys": keys,
435 });
436
437 Raw::from_json(to_raw_value(&key).expect("cross-signing key should serialize"))
438 }
439
440 fn assert_invalid(result: &Result<ValidatedKeys<'_>>) {
441 assert!(matches!(result, Err(Error::Request(InvalidParam, ..))));
442 }
443}