1#![allow(
12 clippy::allow_attributes,
13 reason = "This source file contains enums which allow `clippy::missing_docs_in_private_items`. But an exception to this lint will mistakenly report that it's unfulfilled when used with expect, even though it is not."
14)]
15
16use std::collections::BTreeMap;
17
18use arminius_lib_authentication::SessionInfo;
19use arminius_lib_component::{
20 Component, ServerState,
21 error::{
22 BadJson, InputJsonError, InternalServerError, MatrixApiError, ResultExt as _,
23 create_custom_matrix_error,
24 },
25 ruma_requests::ArminiusResponse,
26};
27use arminius_lib_db::{Database as _, DefaultDb};
28use arminius_lib_db_keys::{CrossSigningKeyRow, DeviceKeysRow};
29use arminius_lib_db_signatures::LocalSignatureQueries as _;
30use arminius_lib_model::user_keys::DbKeyType;
31use arminius_lib_user_keys::{
32 identity_keys::get_local_keys,
33 signatures::{RequiredSigners, SignatureVerificationError, verify_signatures},
34 traits::{SignedObject, SigningKey as _},
35};
36use axum::{Router, extract::State, http::StatusCode, routing::post};
37use itertools::Itertools as _;
38use ruma::{
39 Base64PublicKeyOrDeviceId, OwnedCrossSigningOrDeviceSigningKeyId, OwnedDeviceId, OwnedUserId,
40 UserId,
41 api::{
42 client::keys::upload_signatures,
43 error::{Error as MatrixError, ErrorCode, ErrorKind},
44 },
45 encryption::{CrossSigningKey, DeviceKeys, KeyUsage},
46 serde::{Base64, base64},
47 signatures::PublicKeyMap,
48};
49
50use crate::client_request::AuthenticatedClientRequest;
51
52#[derive(Debug, thiserror::Error)]
54#[error("Invalid signature: {0}")]
55pub struct SignatureError(String);
56
57impl MatrixApiError for SignatureError {
60 fn create_matrix_error(&self) -> MatrixError {
61 create_custom_matrix_error(
62 StatusCode::BAD_REQUEST,
63 "M_INVALID_SIGNATURE",
64 &self.to_string(),
65 )
66 }
67}
68
69impl From<SignatureVerificationError> for SignaturesUploadError {
72 fn from(signature_error: SignatureVerificationError) -> Self {
73 match signature_error {
74 SignatureVerificationError::BadJson(bad_json) => {
75 Self::InputJsonError(InputJsonError::BadJson(bad_json))
76 }
77 other_error => Self::SignatureVerification(SignatureError(other_error.to_string())),
78 }
79 }
80}
81
82#[allow(
83 clippy::missing_docs_in_private_items,
84 reason = "TODO(#2003): This exception predates us enabling `clippy::allow_attributes_without_reason`, so we don't know why it was enabled"
85)]
86#[derive(Debug, thiserror::Error, MatrixApiError)]
87enum SignaturesUploadError {
88 #[error(transparent)]
89 InternalServerError(#[from] InternalServerError),
90
91 #[error(transparent)]
92 InputJsonError(#[from] InputJsonError),
93
94 #[error("Invalid base64: {0}")]
96 #[matrix_api_error(status_code = StatusCode::BAD_REQUEST, error_kind = ErrorKind::Unknown)]
97 InvalidBase64(#[from] ruma::serde::Base64DecodeError),
98
99 #[error(transparent)]
100 SignatureVerification(#[from] SignatureError),
101}
102
103fn new_failure(
106 errcode: &ErrorCode,
107 message: &str,
108) -> Result<upload_signatures::v3::Failure, InternalServerError> {
109 serde_json::from_str(&format!("{{\"errcode\":\"{}\",\"error\":\"{}\"}}", errcode, message))
110 .unrecoverable_error("Failed to create Failure")
111}
112
113type Failures = BTreeMap<OwnedUserId, BTreeMap<String, upload_signatures::v3::Failure>>;
118
119type SignedKeysList = (
132 Vec<(String, DeviceKeys)>,
133 Option<(String, CrossSigningKey)>,
134 BTreeMap<OwnedUserId, BTreeMap<String, CrossSigningKey>>,
135);
136
137#[derive(Debug)]
139pub struct ValidSignature {
140 pub signing_key_type: DbKeyType,
142 pub signing_key_id: OwnedCrossSigningOrDeviceSigningKeyId,
144 pub signed_key_nid: i64,
147 pub signature: Vec<u8>,
149}
150
151fn separate_signatures(
163 request_user_id: &UserId,
164 signed_keys: &BTreeMap<OwnedUserId, upload_signatures::v3::SignedKeys>,
165) -> Result<SignedKeysList, SignaturesUploadError> {
166 let mut self_device_keys_signatures = Vec::new();
167 let mut self_master_key_signatures = None;
168 let mut other_master_key_signatures: BTreeMap<OwnedUserId, BTreeMap<String, CrossSigningKey>> =
169 BTreeMap::new();
170
171 for (signed_user_id, user_signed_keys) in signed_keys {
172 for (key_id, signed_object) in user_signed_keys {
173 let key_id = key_id.to_owned();
174 if let Ok(signed_device_keys) = serde_json::from_str::<DeviceKeys>(signed_object.get())
176 {
177 if signed_user_id == request_user_id {
178 self_device_keys_signatures.push((key_id, signed_device_keys));
179 } else {
180 return Err(InputJsonError::BadJson(BadJson(
181 "Cannot upload device-keys signatures for other users".to_owned(),
182 ))
183 .into());
184 }
185 }
186 else if let Ok(signed_cross_signing_key) =
188 serde_json::from_str::<CrossSigningKey>(signed_object.get())
189 {
190 let Ok(key_usage) = signed_cross_signing_key.usage.iter().exactly_one() else {
191 return Err(InputJsonError::BadJson(BadJson(
192 "Cross-signing key `usage` should contain exactly one entry".to_owned(),
193 ))
194 .into());
195 };
196
197 if key_usage != &KeyUsage::Master {
198 return Err(InputJsonError::BadJson(BadJson(
199 "Cannot upload signatures for a cross-signing key that is not a Master key"
200 .to_owned(),
201 ))
202 .into());
203 }
204
205 if signed_user_id == request_user_id {
208 self_master_key_signatures = Some((key_id, signed_cross_signing_key));
209 } else {
210 let user_keys =
211 other_master_key_signatures.entry(signed_user_id.to_owned()).or_default();
212 user_keys.insert(key_id, signed_cross_signing_key);
213 }
214 }
215 else {
217 return Err(InputJsonError::BadJson(BadJson(
218 "Signed objects must be either device keys or cross-signing keys.".to_owned(),
219 ))
220 .into());
221 }
222 }
223 }
224
225 Ok((self_device_keys_signatures, self_master_key_signatures, other_master_key_signatures))
226}
227
228fn check_signing(
233 keymaps: Vec<&PublicKeyMap>,
234 signed_key: &impl SignedObject<OwnedUserId, Base64PublicKeyOrDeviceId>,
235 required_signers: Option<&RequiredSigners<OwnedUserId, Base64PublicKeyOrDeviceId>>,
236) -> Result<Option<upload_signatures::v3::Failure>, SignaturesUploadError> {
237 if let Err(err) = verify_signatures(keymaps, signed_key, required_signers) {
238 let failure = match err {
239 SignatureVerificationError::BadJson(bad_json) => {
240 new_failure(&ErrorCode::BadJson, &bad_json.0)?
241 }
242 other_error => {
243 new_failure(&ErrorCode::from("M_INVALID_SIGNATURE"), &other_error.to_string())?
244 }
245 };
246
247 return Ok(Some(failure));
248 }
249
250 Ok(None)
251}
252
253#[expect(
266 clippy::too_many_lines,
267 reason = "It is too soon to factor out common lines with other functions"
268)]
269fn process_device_keys_signatures(
270 signing_user_id: &UserId,
271 self_signing_key: Option<&CrossSigningKeyRow>,
272 stored_device_keys: &BTreeMap<OwnedDeviceId, DeviceKeysRow>,
273 new_device_keys: Vec<(String, DeviceKeys)>,
274) -> Result<(Vec<ValidSignature>, Failures), SignaturesUploadError> {
275 let Some(self_signing_key) = self_signing_key else {
277 let failure = new_failure(
278 &ErrorCode::BadJson,
279 "Missing self-signing key to verify device keys signatures",
280 )?;
281
282 let failures = BTreeMap::from([(
283 signing_user_id.to_owned(),
284 BTreeMap::from_iter(
285 new_device_keys.into_iter().map(|(key_id, _key)| (key_id, failure.clone())),
286 ),
287 )]);
288
289 return Ok((Vec::new(), failures));
290 };
291
292 let signing_key_id = self_signing_key
293 .key
294 .key_id()
295 .unrecoverable_error("Unable to get key ID from stored self-signing key")?;
296
297 let mut valid_signatures = Vec::new();
298 let mut user_failures = BTreeMap::new();
299
300 for (signed_device_keys_id, mut signed_device_keys) in new_device_keys {
301 let Some(stored_device_keys) = stored_device_keys.get(&signed_device_keys.device_id) else {
303 user_failures.insert(
304 signed_device_keys_id,
305 new_failure(&ErrorCode::BadJson, "No matching device keys found")?,
306 );
307
308 continue;
309 };
310
311 if !stored_device_keys
313 .key
314 .compare(&signed_device_keys)
315 .unrecoverable_error("Failed to compare keys")?
316 {
317 user_failures.insert(
318 signed_device_keys_id,
319 new_failure(&ErrorCode::BadJson, "Signed device keys does not match stored one")?,
320 );
321
322 continue;
323 }
324
325 if stored_device_keys
327 .key
328 .signatures
329 .get(signing_user_id)
330 .map(|user_signatures| user_signatures.contains_key(&signing_key_id))
331 == Some(true)
332 {
333 continue;
334 }
335
336 let Some(signature) = signed_device_keys
338 .signatures
339 .get(signing_user_id)
340 .and_then(|user_signatures| user_signatures.get(&signing_key_id))
341 .cloned()
342 else {
343 user_failures.insert(
344 signed_device_keys_id.clone(),
345 new_failure(
346 &ErrorCode::BadJson,
347 &format!(
348 "Device keys '{}' should contain a signature from '{}'",
349 signed_device_keys_id, signing_key_id
350 ),
351 )?,
352 );
353
354 continue;
355 };
356
357 let device_keys_id = stored_device_keys
358 .key
359 .key_id()
360 .unrecoverable_error("Failed to get key ID of stored keys")?;
361 if signed_device_keys
362 .signatures
363 .get(&signed_device_keys.user_id)
364 .and_then(|user_sigs| user_sigs.get(&device_keys_id))
365 .is_none()
366 {
367 let self_signature = stored_device_keys
368 .key
369 .signatures
370 .get(&stored_device_keys.key.user_id)
371 .and_then(|user_sigs| user_sigs.get(&device_keys_id))
372 .unrecoverable_error("We store device keys that are not signed by themselves")?;
373
374 signed_device_keys.signatures.insert_signature(
375 signed_device_keys.user_id.clone(),
376 device_keys_id,
377 self_signature.to_owned(),
378 );
379 }
380
381 if let Some(failure) = check_signing(
384 vec![
385 &self_signing_key.key.keymap().map_err(InputJsonError::BadJson)?,
386 &signed_device_keys.keymap().map_err(InputJsonError::BadJson)?,
387 ],
388 &signed_device_keys,
389 Some(&BTreeMap::from([(
390 signing_user_id.to_owned(),
391 vec![signed_device_keys.key_id().map_err(InputJsonError::BadJson)?],
392 )])),
393 )? {
394 user_failures.insert(signed_device_keys_id, failure);
395 continue;
396 }
397
398 let signature: Vec<u8> = Base64::<base64::Standard>::parse(signature)?.as_bytes().to_vec();
399
400 valid_signatures.push(ValidSignature {
401 signing_key_type: DbKeyType::SelfSigning,
402 signing_key_id: signing_key_id.clone(),
403 signed_key_nid: stored_device_keys.nid,
404 signature,
405 });
406 }
407
408 if !user_failures.is_empty() {
409 return Ok((
410 valid_signatures,
411 BTreeMap::from([(signing_user_id.to_owned(), user_failures)]),
412 ));
413 }
414
415 Ok((valid_signatures, BTreeMap::new()))
416}
417
418fn process_self_master_key_signatures(
430 session: &SessionInfo,
431 device_keys: &BTreeMap<OwnedDeviceId, DeviceKeysRow>,
432 stored_master_key: Option<&CrossSigningKeyRow>,
433 new_master_key: &CrossSigningKey,
434) -> Result<(Option<ValidSignature>, Option<upload_signatures::v3::Failure>), SignaturesUploadError>
435{
436 let Some(signing_device_keys) = device_keys.get(&session.device_id) else {
438 return Ok((
439 None,
440 Some(new_failure(&ErrorCode::BadJson, "No device keys found to verify signature")?),
441 ));
442 };
443
444 let Some(stored_master_key) = stored_master_key else {
446 return Ok((None, Some(new_failure(&ErrorCode::BadJson, "No matching Master key found")?)));
447 };
448
449 if !stored_master_key
451 .key
452 .compare(new_master_key)
453 .unrecoverable_error("Failed to compare keys")?
454 {
455 return Ok((
456 None,
457 Some(new_failure(&ErrorCode::BadJson, "Signed Master key does not match stored one")?),
458 ));
459 }
460
461 let signing_key_id =
462 signing_device_keys.key.key_id().unrecoverable_error("Failed to get key ID")?;
463
464 if stored_master_key
466 .key
467 .signatures
468 .get(&session.user_id)
469 .map(|user_signatures| user_signatures.contains_key(&signing_key_id))
470 == Some(true)
471 {
472 return Ok((None, None));
473 }
474
475 let Some(signature) = new_master_key
477 .signatures
478 .get(&session.user_id)
479 .and_then(|user_signatures| user_signatures.get(&signing_key_id))
480 else {
481 return Ok((
482 None,
483 Some(new_failure(
484 &ErrorCode::BadJson,
485 "Master key is not signed by the requesting user's device",
486 )?),
487 ));
488 };
489
490 if let Some(failure) = check_signing(
492 vec![&signing_device_keys.key.keymap().map_err(InputJsonError::BadJson)?],
493 new_master_key,
494 None,
495 )? {
496 return Ok((None, Some(failure)));
497 }
498
499 let signature: Vec<u8> = Base64::<base64::Standard>::parse(signature)?.as_bytes().to_vec();
500
501 Ok((
502 Some(ValidSignature {
503 signing_key_type: DbKeyType::Device,
504 signing_key_id,
505 signed_key_nid: stored_master_key.nid,
506 signature,
507 }),
508 None,
509 ))
510}
511
512fn process_other_master_key_signatures(
525 signing_user_id: &UserId,
526 user_signing_key: Option<&CrossSigningKeyRow>,
527 stored_master_keys: &BTreeMap<OwnedUserId, CrossSigningKeyRow>,
528 new_master_keys: &BTreeMap<OwnedUserId, BTreeMap<String, CrossSigningKey>>,
529) -> Result<(Vec<ValidSignature>, Failures), SignaturesUploadError> {
530 let Some(user_signing_key) = user_signing_key else {
532 let failure = new_failure(
533 &ErrorCode::BadJson,
534 "Missing user-signing key to verify other user master key signatures",
535 )?;
536
537 let failures = BTreeMap::from_iter(new_master_keys.iter().map(|(user_id, user_keys)| {
538 (
539 user_id.to_owned(),
540 BTreeMap::from_iter(
541 user_keys.keys().cloned().map(|key_id| (key_id, failure.clone())),
542 ),
543 )
544 }));
545
546 return Ok((Vec::new(), failures));
547 };
548
549 let signing_key_id =
550 user_signing_key.key.key_id().unrecoverable_error("Failed to get key ID")?;
551
552 let mut valid_signatures = Vec::new();
553 let mut failures = BTreeMap::new();
554
555 for (signed_user_id, signed_keys) in new_master_keys {
556 let Some(stored_master_key) = stored_master_keys.get(signed_user_id) else {
558 let failure = new_failure(&ErrorCode::BadJson, "Missing matching master key")?;
559 failures.insert(
560 signed_user_id.to_owned(),
561 BTreeMap::from_iter(
562 signed_keys.keys().cloned().map(|key_id| (key_id, failure.clone())),
563 ),
564 );
565
566 continue;
567 };
568
569 for (signed_key_id, signed_master_key) in signed_keys {
570 if !stored_master_key
572 .key
573 .compare(signed_master_key)
574 .unrecoverable_error("Failed to compare keys")?
575 {
576 failures.insert(
577 signed_user_id.to_owned(),
578 BTreeMap::from([(
579 signed_key_id.to_owned(),
580 new_failure(&ErrorCode::BadJson, "Master key does not match stored one")?,
581 )]),
582 );
583
584 continue;
585 }
586
587 let Some(signature) = signed_master_key
589 .signatures
590 .get(signing_user_id)
591 .and_then(|user_signatures| user_signatures.get(&signing_key_id))
592 .cloned()
593 else {
594 failures.insert(
595 signed_user_id.to_owned(),
596 BTreeMap::from([(
597 signed_key_id.to_owned(),
598 new_failure(
599 &ErrorCode::BadJson,
600 &format!(
601 "Master key '{}' is missing a signature from '{}'",
602 signed_key_id, signing_key_id
603 ),
604 )?,
605 )]),
606 );
607
608 continue;
609 };
610
611 if let Some(failure) = check_signing(
613 vec![&user_signing_key.key.keymap().map_err(InputJsonError::BadJson)?],
614 signed_master_key,
615 None,
616 )? {
617 failures.insert(
618 signed_user_id.to_owned(),
619 BTreeMap::from([(signed_key_id.to_owned(), failure)]),
620 );
621
622 continue;
623 }
624
625 let signature: Vec<u8> =
626 Base64::<base64::Standard>::parse(signature)?.as_bytes().to_vec();
627
628 valid_signatures.push(ValidSignature {
629 signing_key_type: DbKeyType::UserSigning,
630 signing_key_id: signing_key_id.clone(),
631 signed_key_nid: stored_master_key.nid,
632 signature,
633 });
634 }
635 }
636
637 Ok((valid_signatures, failures))
638}
639
640async fn upload_signatures_handler(
652 mut database: State<DefaultDb>,
653 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
654 upload_signatures::v3::Request,
655 >,
656) -> Result<ArminiusResponse<upload_signatures::v3::Response>, SignaturesUploadError> {
657 let (self_device_keys_signatures, self_master_key_signatures, other_users_master_keys) =
659 separate_signatures(&claims.user_id, &request.signed_keys)?;
660
661 let stored_keys = get_local_keys(
663 &mut database,
664 Some(&claims.user_id.clone()),
665 std::iter::once((claims.user_id.clone(), Vec::new()))
666 .chain(
667 other_users_master_keys
668 .keys()
669 .map(|other_user_id| (other_user_id.clone(), Vec::new())),
670 )
671 .collect(),
672 )
673 .await?;
674
675 let mut valid_signatures = Vec::new();
676 let mut failures = BTreeMap::new();
677
678 if !self_device_keys_signatures.is_empty() {
680 let (new_valid_signatures, new_failures) = process_device_keys_signatures(
681 &claims.user_id,
682 stored_keys.self_signing_keys.get(&claims.user_id),
683 stored_keys.device_keys.get(&claims.user_id).unwrap_or(&BTreeMap::new()),
684 self_device_keys_signatures,
685 )?;
686 valid_signatures.extend(new_valid_signatures);
687 failures.extend(new_failures);
688 }
689
690 if let Some((signed_master_key_id, signed_master_key)) = self_master_key_signatures {
692 let (new_valid_signature, new_invalid_signature) = process_self_master_key_signatures(
693 &claims,
694 stored_keys.device_keys.get(&claims.user_id).unwrap_or(&BTreeMap::new()),
695 stored_keys.master_keys.get(&claims.user_id),
696 &signed_master_key,
697 )?;
698
699 if let Some(valid_signature) = new_valid_signature {
700 valid_signatures.push(valid_signature);
701 }
702 if let Some(failure) = new_invalid_signature {
703 failures
704 .insert(claims.user_id.clone(), BTreeMap::from([(signed_master_key_id, failure)]));
705 }
706 }
707
708 if !other_users_master_keys.is_empty() {
710 let (new_valid_signatures, new_failures) = process_other_master_key_signatures(
711 &claims.user_id,
712 stored_keys.user_signing_keys.get(&claims.user_id),
713 &stored_keys.master_keys,
714 &other_users_master_keys,
715 )?;
716 valid_signatures.extend(new_valid_signatures);
717 failures.extend(new_failures);
718 }
719
720 let mut transaction =
722 database.begin_transaction().await.unrecoverable_error("Failed to start transaction")?;
723 for ValidSignature { signing_key_type, signing_key_id, signed_key_nid, signature } in
724 valid_signatures
725 {
726 transaction
727 .upsert_local_signature(
728 &claims.user_id,
729 signing_key_type,
730 &signing_key_id,
731 signed_key_nid,
732 &signature,
733 )
734 .await
735 .unrecoverable_error("Failed to insert valid cross-signing signatures")?;
736 }
737 transaction.commit().await.unrecoverable_error("Failed to commit transaction")?;
738
739 let mut response = upload_signatures::v3::Response::new();
741 response.failures = failures;
742 Ok(response.into())
743}
744
745#[derive(Debug)]
747pub struct SignaturesComponent;
748
749impl Component for SignaturesComponent {
750 fn router() -> Router<ServerState> {
751 Router::new()
752 .route("/_matrix/client/v3/keys/signatures/upload", post(upload_signatures_handler))
753 }
754}