Skip to main content

tuwunel_api_client/keys/
upload_signing_keys.rs

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