1use std::{
16 collections::{BTreeMap, BTreeSet, HashMap},
17 default::Default,
18};
19
20use itertools::{Either, Itertools};
21use matrix_sdk_common::deserialized_responses::WithheldCode;
22use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId};
23use serde::{Deserialize, Serialize};
24use tracing::{debug, instrument, trace};
25
26use super::OutboundGroupSession;
27use crate::{
28 error::{OlmResult, SessionRecipientCollectionError},
29 olm::ShareInfo,
30 store::Store,
31 DeviceData, EncryptionSettings, LocalTrust, OlmError, OwnUserIdentityData, UserIdentityData,
32};
33#[cfg(doc)]
34use crate::{Device, UserIdentity};
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
39#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
40#[serde(from = "CollectStrategyDeserializationHelper")]
41pub enum CollectStrategy {
42 #[default]
44 AllDevices,
45
46 ErrorOnVerifiedUserProblem,
61
62 IdentityBasedStrategy,
66
67 OnlyTrustedDevices,
75}
76
77impl CollectStrategy {
78 pub const fn new_identity_based() -> Self {
80 CollectStrategy::IdentityBasedStrategy
81 }
82}
83
84#[derive(Deserialize)]
86enum CollectStrategyDeserializationHelper {
87 DeviceBasedStrategy {
90 #[serde(default)]
91 error_on_verified_user_problem: bool,
92
93 #[serde(default)]
94 only_allow_trusted_devices: bool,
95 },
96
97 AllDevices,
98 ErrorOnVerifiedUserProblem,
99 IdentityBasedStrategy,
100 OnlyTrustedDevices,
101}
102
103impl From<CollectStrategyDeserializationHelper> for CollectStrategy {
104 fn from(value: CollectStrategyDeserializationHelper) -> Self {
105 use CollectStrategyDeserializationHelper::*;
106
107 match value {
108 DeviceBasedStrategy {
109 only_allow_trusted_devices: true,
110 error_on_verified_user_problem: _,
111 } => CollectStrategy::OnlyTrustedDevices,
112 DeviceBasedStrategy {
113 only_allow_trusted_devices: false,
114 error_on_verified_user_problem: true,
115 } => CollectStrategy::ErrorOnVerifiedUserProblem,
116 DeviceBasedStrategy {
117 only_allow_trusted_devices: false,
118 error_on_verified_user_problem: false,
119 } => CollectStrategy::AllDevices,
120
121 AllDevices => CollectStrategy::AllDevices,
122 ErrorOnVerifiedUserProblem => CollectStrategy::ErrorOnVerifiedUserProblem,
123 IdentityBasedStrategy => CollectStrategy::IdentityBasedStrategy,
124 OnlyTrustedDevices => CollectStrategy::OnlyTrustedDevices,
125 }
126 }
127}
128
129#[derive(Debug, Default)]
136pub(crate) struct CollectRecipientsResult {
137 pub should_rotate: bool,
139 pub devices: BTreeMap<OwnedUserId, Vec<DeviceData>>,
141 pub withheld_devices: Vec<(DeviceData, WithheldCode)>,
144}
145
146#[instrument(skip_all)]
153pub(crate) async fn collect_session_recipients(
154 store: &Store,
155 users: impl Iterator<Item = &UserId>,
156 settings: &EncryptionSettings,
157 outbound: &OutboundGroupSession,
158) -> OlmResult<CollectRecipientsResult> {
159 let mut result = collect_recipients_for_share_strategy(
160 store,
161 users,
162 &settings.sharing_strategy,
163 Some(outbound),
164 )
165 .await?;
166
167 let device_removed = result.should_rotate;
177
178 let visibility_changed = outbound.settings().history_visibility != settings.history_visibility;
179 let algorithm_changed = outbound.settings().algorithm != settings.algorithm;
180
181 result.should_rotate = device_removed || visibility_changed || algorithm_changed;
182
183 if result.should_rotate {
184 debug!(
185 device_removed,
186 visibility_changed, algorithm_changed, "Rotating room key to protect room history",
187 );
188 }
189
190 Ok(result)
191}
192
193pub(crate) async fn collect_recipients_for_share_strategy(
203 store: &Store,
204 users: impl Iterator<Item = &UserId>,
205 share_strategy: &CollectStrategy,
206 outbound: Option<&OutboundGroupSession>,
207) -> OlmResult<CollectRecipientsResult> {
208 let users: BTreeSet<&UserId> = users.collect();
209 trace!(?users, ?share_strategy, "Calculating group session recipients");
210
211 let mut result = CollectRecipientsResult::default();
212 let mut verified_users_with_new_identities: Vec<OwnedUserId> = Default::default();
213
214 if let Some(outbound) = outbound {
218 let view = outbound.sharing_view();
219 let users_shared_with = view.shared_with_users().collect::<BTreeSet<_>>();
220 let left_users = users_shared_with.difference(&users).collect::<BTreeSet<_>>();
221 if !left_users.is_empty() {
222 trace!(?left_users, "Some users have left the chat: session must be rotated");
223 result.should_rotate = true;
224 }
225 }
226
227 let own_identity = store.get_user_identity(store.user_id()).await?.and_then(|i| i.into_own());
228
229 match share_strategy {
231 CollectStrategy::AllDevices => {
232 for user_id in users {
233 trace!(?user_id, "CollectStrategy::AllDevices: Considering recipient devices",);
234 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
235 let device_owner_identity = store.get_user_identity(user_id).await?;
236
237 let recipient_devices = split_devices_for_user_for_all_devices_strategy(
238 user_devices,
239 &own_identity,
240 &device_owner_identity,
241 );
242 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
243 }
244 }
245 CollectStrategy::ErrorOnVerifiedUserProblem => {
246 let mut unsigned_devices_of_verified_users: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>> =
247 Default::default();
248
249 for user_id in users {
250 trace!(
251 ?user_id,
252 "CollectStrategy::ErrorOnVerifiedUserProblem: Considering recipient devices"
253 );
254 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
255
256 let device_owner_identity = store.get_user_identity(user_id).await?;
257
258 if has_identity_verification_violation(
259 own_identity.as_ref(),
260 device_owner_identity.as_ref(),
261 ) {
262 verified_users_with_new_identities.push(user_id.to_owned());
263 continue;
265 }
266
267 let recipient_devices =
268 split_devices_for_user_for_error_on_verified_user_problem_strategy(
269 user_devices,
270 &own_identity,
271 &device_owner_identity,
272 );
273
274 match recipient_devices {
275 ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices) => {
276 unsigned_devices_of_verified_users.insert(user_id.to_owned(), devices);
277 }
278 ErrorOnVerifiedUserProblemResult::Devices(recipient_devices) => {
279 update_recipients_for_user(
280 &mut result,
281 outbound,
282 user_id,
283 recipient_devices,
284 );
285 }
286 }
287 }
288
289 if !unsigned_devices_of_verified_users.is_empty() {
293 return Err(OlmError::SessionRecipientCollectionError(
294 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
295 unsigned_devices_of_verified_users,
296 ),
297 ));
298 }
299 }
300 CollectStrategy::IdentityBasedStrategy => {
301 match &own_identity {
304 None => {
305 return Err(OlmError::SessionRecipientCollectionError(
306 SessionRecipientCollectionError::CrossSigningNotSetup,
307 ))
308 }
309 Some(identity) if !identity.is_verified() => {
310 return Err(OlmError::SessionRecipientCollectionError(
311 SessionRecipientCollectionError::SendingFromUnverifiedDevice,
312 ))
313 }
314 Some(_) => (),
315 }
316
317 for user_id in users {
318 trace!(
319 ?user_id,
320 "CollectStrategy::IdentityBasedStrategy: Considering recipient devices"
321 );
322 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
323
324 let device_owner_identity = store.get_user_identity(user_id).await?;
325
326 if has_identity_verification_violation(
327 own_identity.as_ref(),
328 device_owner_identity.as_ref(),
329 ) {
330 verified_users_with_new_identities.push(user_id.to_owned());
331 continue;
333 }
334
335 let recipient_devices = split_devices_for_user_for_identity_based_strategy(
336 user_devices,
337 &device_owner_identity,
338 );
339
340 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
341 }
342 }
343
344 CollectStrategy::OnlyTrustedDevices => {
345 for user_id in users {
346 trace!(
347 ?user_id,
348 "CollectStrategy::OnlyTrustedDevices: Considering recipient devices"
349 );
350 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
351 let device_owner_identity = store.get_user_identity(user_id).await?;
352
353 let recipient_devices = split_devices_for_user_for_only_trusted_devices(
354 user_devices,
355 &own_identity,
356 &device_owner_identity,
357 );
358
359 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
360 }
361 }
362 }
363
364 if !verified_users_with_new_identities.is_empty() {
367 return Err(OlmError::SessionRecipientCollectionError(
368 SessionRecipientCollectionError::VerifiedUserChangedIdentity(
369 verified_users_with_new_identities,
370 ),
371 ));
372 }
373
374 trace!(result.should_rotate, "Done calculating group session recipients");
375
376 Ok(result)
377}
378
379fn update_recipients_for_user(
382 recipients: &mut CollectRecipientsResult,
383 outbound: Option<&OutboundGroupSession>,
384 user_id: &UserId,
385 recipient_devices: RecipientDevicesForUser,
386) {
387 if let Some(outbound) = outbound {
392 if !recipients.should_rotate {
393 recipients.should_rotate = is_session_overshared_for_user(
394 outbound,
395 user_id,
396 &recipient_devices.allowed_devices,
397 )
398 }
399 }
400
401 recipients
402 .devices
403 .entry(user_id.to_owned())
404 .or_default()
405 .extend(recipient_devices.allowed_devices);
406 recipients.withheld_devices.extend(recipient_devices.denied_devices_with_code);
407}
408
409fn is_session_overshared_for_user(
425 outbound_session: &OutboundGroupSession,
426 user_id: &UserId,
427 recipient_devices: &[DeviceData],
428) -> bool {
429 let recipient_device_ids: BTreeSet<&DeviceId> =
431 recipient_devices.iter().map(|d| d.device_id()).collect();
432
433 let view = outbound_session.sharing_view();
434 let newly_deleted_or_blacklisted: BTreeSet<&DeviceId> = view
435 .iter_shares(Some(user_id), None)
436 .filter_map(|(_user_id, device_id, info)| {
437 if matches!(info, ShareInfo::Shared(_)) && !recipient_device_ids.contains(device_id) {
441 Some(device_id)
442 } else {
443 None
444 }
445 })
446 .collect();
447
448 let should_rotate = !newly_deleted_or_blacklisted.is_empty();
449 if should_rotate {
450 debug!(
451 "Rotating a room key due to these devices being deleted/blacklisted {:?}",
452 newly_deleted_or_blacklisted,
453 );
454 }
455 should_rotate
456}
457
458#[derive(Default)]
465struct RecipientDevicesForUser {
466 allowed_devices: Vec<DeviceData>,
468 denied_devices_with_code: Vec<(DeviceData, WithheldCode)>,
470}
471
472enum ErrorOnVerifiedUserProblemResult {
475 UnsignedDevicesOfVerifiedUser(Vec<OwnedDeviceId>),
479
480 Devices(RecipientDevicesForUser),
482}
483
484fn split_devices_for_user_for_all_devices_strategy(
487 user_devices: HashMap<OwnedDeviceId, DeviceData>,
488 own_identity: &Option<OwnUserIdentityData>,
489 device_owner_identity: &Option<UserIdentityData>,
490) -> RecipientDevicesForUser {
491 let (left, right) = user_devices.into_values().partition_map(|d| {
492 if d.is_blacklisted() {
493 Either::Right((d, WithheldCode::Blacklisted))
494 } else if d.is_dehydrated()
495 && should_withhold_to_dehydrated_device(
496 &d,
497 own_identity.as_ref(),
498 device_owner_identity.as_ref(),
499 )
500 {
501 Either::Right((d, WithheldCode::Unverified))
502 } else {
503 Either::Left(d)
504 }
505 });
506
507 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
508}
509
510fn should_withhold_to_dehydrated_device(
519 device: &DeviceData,
520 own_identity: Option<&OwnUserIdentityData>,
521 device_owner_identity: Option<&UserIdentityData>,
522) -> bool {
523 device_owner_identity.is_none_or(|owner_id| {
524 !device.is_cross_signed_by_owner(owner_id) ||
526
527 (owner_id.was_previously_verified() && !is_user_verified(own_identity, owner_id))
529 })
530}
531
532fn split_devices_for_user_for_error_on_verified_user_problem_strategy(
545 user_devices: HashMap<OwnedDeviceId, DeviceData>,
546 own_identity: &Option<OwnUserIdentityData>,
547 device_owner_identity: &Option<UserIdentityData>,
548) -> ErrorOnVerifiedUserProblemResult {
549 let mut recipient_devices = RecipientDevicesForUser::default();
550
551 let mut unsigned_devices_of_verified_users: Option<Vec<OwnedDeviceId>> = None;
554
555 for d in user_devices.into_values() {
556 match handle_device_for_user_for_error_on_verified_user_problem_strategy(
557 &d,
558 own_identity.as_ref(),
559 device_owner_identity.as_ref(),
560 ) {
561 ErrorOnVerifiedUserProblemDeviceDecision::Ok => {
562 recipient_devices.allowed_devices.push(d)
563 }
564 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => {
565 recipient_devices.denied_devices_with_code.push((d, code))
566 }
567 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
568 unsigned_devices_of_verified_users
569 .get_or_insert_with(Vec::default)
570 .push(d.device_id().to_owned())
571 }
572 }
573 }
574
575 if let Some(devices) = unsigned_devices_of_verified_users {
576 ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices)
577 } else {
578 ErrorOnVerifiedUserProblemResult::Devices(recipient_devices)
579 }
580}
581
582enum ErrorOnVerifiedUserProblemDeviceDecision {
585 Ok,
586 Withhold(WithheldCode),
587 UnsignedOfVerified,
588}
589
590fn handle_device_for_user_for_error_on_verified_user_problem_strategy(
591 device: &DeviceData,
592 own_identity: Option<&OwnUserIdentityData>,
593 device_owner_identity: Option<&UserIdentityData>,
594) -> ErrorOnVerifiedUserProblemDeviceDecision {
595 if device.is_blacklisted() {
596 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Blacklisted)
597 } else if device.local_trust_state() == LocalTrust::Ignored {
598 ErrorOnVerifiedUserProblemDeviceDecision::Ok
600 } else if is_unsigned_device_of_verified_user(own_identity, device_owner_identity, device) {
601 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified
602 } else if device.is_dehydrated()
603 && device_owner_identity.is_none_or(|owner_id| {
604 !device.is_cross_signed_by_owner(owner_id)
607 })
608 {
609 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Unverified)
610 } else {
611 ErrorOnVerifiedUserProblemDeviceDecision::Ok
612 }
613}
614
615fn split_devices_for_user_for_identity_based_strategy(
616 user_devices: HashMap<OwnedDeviceId, DeviceData>,
617 device_owner_identity: &Option<UserIdentityData>,
618) -> RecipientDevicesForUser {
619 match device_owner_identity {
620 None => {
621 RecipientDevicesForUser {
624 allowed_devices: Vec::default(),
625 denied_devices_with_code: user_devices
626 .into_values()
627 .map(|d| (d, WithheldCode::Unverified))
628 .collect(),
629 }
630 }
631 Some(device_owner_identity) => {
632 let (recipients, withheld_recipients): (
634 Vec<DeviceData>,
635 Vec<(DeviceData, WithheldCode)>,
636 ) = user_devices.into_values().partition_map(|d| {
637 if d.is_cross_signed_by_owner(device_owner_identity) {
638 Either::Left(d)
639 } else {
640 Either::Right((d, WithheldCode::Unverified))
641 }
642 });
643 RecipientDevicesForUser {
644 allowed_devices: recipients,
645 denied_devices_with_code: withheld_recipients,
646 }
647 }
648 }
649}
650
651fn split_devices_for_user_for_only_trusted_devices(
654 user_devices: HashMap<OwnedDeviceId, DeviceData>,
655 own_identity: &Option<OwnUserIdentityData>,
656 device_owner_identity: &Option<UserIdentityData>,
657) -> RecipientDevicesForUser {
658 let (left, right) = user_devices.into_values().partition_map(|d| {
659 match (
660 d.local_trust_state(),
661 d.is_cross_signing_trusted(own_identity, device_owner_identity),
662 ) {
663 (LocalTrust::BlackListed, _) => Either::Right((d, WithheldCode::Blacklisted)),
664 (LocalTrust::Ignored | LocalTrust::Verified, _) => Either::Left(d),
665 (LocalTrust::Unset, false) => Either::Right((d, WithheldCode::Unverified)),
666 (LocalTrust::Unset, true) => Either::Left(d),
667 }
668 });
669 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
670}
671
672fn is_unsigned_device_of_verified_user(
673 own_identity: Option<&OwnUserIdentityData>,
674 device_owner_identity: Option<&UserIdentityData>,
675 device_data: &DeviceData,
676) -> bool {
677 device_owner_identity.is_some_and(|device_owner_identity| {
678 is_user_verified(own_identity, device_owner_identity)
679 && !device_data.is_cross_signed_by_owner(device_owner_identity)
680 })
681}
682
683fn has_identity_verification_violation(
690 own_identity: Option<&OwnUserIdentityData>,
691 device_owner_identity: Option<&UserIdentityData>,
692) -> bool {
693 device_owner_identity.is_some_and(|device_owner_identity| {
694 device_owner_identity.was_previously_verified()
695 && !is_user_verified(own_identity, device_owner_identity)
696 })
697}
698
699fn is_user_verified(
700 own_identity: Option<&OwnUserIdentityData>,
701 user_identity: &UserIdentityData,
702) -> bool {
703 match user_identity {
704 UserIdentityData::Own(own_identity) => own_identity.is_verified(),
705 UserIdentityData::Other(other_identity) => {
706 own_identity.is_some_and(|oi| oi.is_identity_verified(other_identity))
707 }
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use std::{collections::BTreeMap, iter, sync::Arc};
714
715 use assert_matches::assert_matches;
716 use assert_matches2::assert_let;
717 use insta::{assert_snapshot, with_settings};
718 use matrix_sdk_common::deserialized_responses::WithheldCode;
719 use matrix_sdk_test::{
720 async_test, test_json,
721 test_json::keys_query_sets::{
722 IdentityChangeDataSet, KeyDistributionTestData, MaloIdentityChangeDataSet,
723 VerificationViolationTestData,
724 },
725 };
726 use ruma::{
727 device_id,
728 events::{dummy::ToDeviceDummyEventContent, room::history_visibility::HistoryVisibility},
729 room_id, TransactionId,
730 };
731 use serde_json::json;
732
733 use crate::{
734 error::SessionRecipientCollectionError,
735 olm::{OutboundGroupSession, ShareInfo},
736 session_manager::{
737 group_sessions::share_strategy::collect_session_recipients, CollectStrategy,
738 },
739 store::caches::SequenceNumber,
740 testing::simulate_key_query_response_for_verification,
741 types::requests::ToDeviceRequest,
742 CrossSigningKeyExport, EncryptionSettings, LocalTrust, OlmError, OlmMachine,
743 };
744
745 async fn test_machine() -> OlmMachine {
749 use KeyDistributionTestData as DataSet;
750
751 let machine = OlmMachine::new(DataSet::me_id(), DataSet::me_device_id()).await;
753 let keys_query = DataSet::me_keys_query_response();
754 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
755
756 machine
758 .import_cross_signing_keys(CrossSigningKeyExport {
759 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
760 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
761 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
762 })
763 .await
764 .unwrap();
765
766 machine
767 }
768
769 async fn import_known_users_to_test_machine(machine: &OlmMachine) {
772 let keys_query = KeyDistributionTestData::dan_keys_query_response();
773 let txn_id = TransactionId::new();
774 machine.mark_request_as_sent(&txn_id, &keys_query).await.unwrap();
775
776 let txn_id_dave = TransactionId::new();
777 let keys_query_dave = KeyDistributionTestData::dave_keys_query_response();
778 machine.mark_request_as_sent(&txn_id_dave, &keys_query_dave).await.unwrap();
779
780 let txn_id_good = TransactionId::new();
781 let keys_query_good = KeyDistributionTestData::good_keys_query_response();
782 machine.mark_request_as_sent(&txn_id_good, &keys_query_good).await.unwrap();
783 }
784
785 #[test]
788 fn test_serialize_device_based_strategy() {
789 let encryption_settings = all_devices_strategy_settings();
790 let serialized = serde_json::to_string(&encryption_settings).unwrap();
791 with_settings!({prepend_module_to_snapshot => false}, {
792 assert_snapshot!(serialized)
793 });
794 }
795
796 #[test]
800 fn test_deserialize_old_device_based_strategy() {
801 let settings: EncryptionSettings = serde_json::from_value(json!({
802 "algorithm": "m.megolm.v1.aes-sha2",
803 "rotation_period":{"secs":604800,"nanos":0},
804 "rotation_period_msgs":100,
805 "history_visibility":"shared",
806 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":false}},
807 })).unwrap();
808 assert_matches!(settings.sharing_strategy, CollectStrategy::AllDevices);
809 }
810
811 #[test]
815 fn test_deserialize_old_error_on_verified_user_problem() {
816 let settings: EncryptionSettings = serde_json::from_value(json!({
817 "algorithm": "m.megolm.v1.aes-sha2",
818 "rotation_period":{"secs":604800,"nanos":0},
819 "rotation_period_msgs":100,
820 "history_visibility":"shared",
821 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":true}},
822 })).unwrap();
823 assert_matches!(settings.sharing_strategy, CollectStrategy::ErrorOnVerifiedUserProblem);
824 }
825
826 #[test]
830 fn test_deserialize_old_only_trusted_devices_strategy() {
831 let settings: EncryptionSettings = serde_json::from_value(json!({
832 "algorithm": "m.megolm.v1.aes-sha2",
833 "rotation_period":{"secs":604800,"nanos":0},
834 "rotation_period_msgs":100,
835 "history_visibility":"shared",
836 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":true,"error_on_verified_user_problem":false}},
837 })).unwrap();
838 assert_matches!(settings.sharing_strategy, CollectStrategy::OnlyTrustedDevices);
839 }
840
841 #[async_test]
842 async fn test_share_with_per_device_strategy_to_all() {
843 let machine = test_machine().await;
844 import_known_users_to_test_machine(&machine).await;
845
846 let encryption_settings = all_devices_strategy_settings();
847
848 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
849
850 let share_result = collect_session_recipients(
851 machine.store(),
852 vec![
853 KeyDistributionTestData::dan_id(),
854 KeyDistributionTestData::dave_id(),
855 KeyDistributionTestData::good_id(),
856 ]
857 .into_iter(),
858 &encryption_settings,
859 &group_session,
860 )
861 .await
862 .unwrap();
863
864 assert!(!share_result.should_rotate);
865
866 let dan_devices_shared =
867 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
868 let dave_devices_shared =
869 share_result.devices.get(KeyDistributionTestData::dave_id()).unwrap();
870 let good_devices_shared =
871 share_result.devices.get(KeyDistributionTestData::good_id()).unwrap();
872
873 assert_eq!(dan_devices_shared.len(), 2);
875 assert_eq!(dave_devices_shared.len(), 1);
876 assert_eq!(good_devices_shared.len(), 2);
877 }
878
879 #[async_test]
880 async fn test_share_with_only_trusted_strategy() {
881 let machine = test_machine().await;
882 import_known_users_to_test_machine(&machine).await;
883
884 let encryption_settings = EncryptionSettings {
885 sharing_strategy: CollectStrategy::OnlyTrustedDevices,
886 ..Default::default()
887 };
888
889 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
890
891 let share_result = collect_session_recipients(
892 machine.store(),
893 vec![
894 KeyDistributionTestData::dan_id(),
895 KeyDistributionTestData::dave_id(),
896 KeyDistributionTestData::good_id(),
897 ]
898 .into_iter(),
899 &encryption_settings,
900 &group_session,
901 )
902 .await
903 .unwrap();
904
905 assert!(!share_result.should_rotate);
906
907 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
908 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
909 assert!(dave_devices_shared.unwrap().is_empty());
911 assert!(good_devices_shared.unwrap().is_empty());
912
913 let dan_devices_shared =
916 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
917
918 assert_eq!(dan_devices_shared.len(), 1);
919 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
920 assert_eq!(
921 dan_device_that_will_get_the_key.device_id().as_str(),
922 KeyDistributionTestData::dan_signed_device_id()
923 );
924
925 let (_, code) = share_result
927 .withheld_devices
928 .iter()
929 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
930 .expect("This dan's device should receive a withheld code");
931
932 assert_eq!(code, &WithheldCode::Unverified);
933
934 let (_, code) = share_result
935 .withheld_devices
936 .iter()
937 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
938 .expect("This daves's device should receive a withheld code");
939
940 assert_eq!(code, &WithheldCode::Unverified);
941 }
942
943 #[async_test]
947 async fn test_error_on_unsigned_of_verified_users() {
948 use VerificationViolationTestData as DataSet;
949
950 let machine = unsigned_of_verified_setup().await;
952
953 let carol_keys = DataSet::carol_keys_query_response_signed();
955 machine.mark_request_as_sent(&TransactionId::new(), &carol_keys).await.unwrap();
956
957 let carol_identity =
959 machine.get_identity(DataSet::carol_id(), None).await.unwrap().unwrap();
960 assert!(carol_identity.other().unwrap().is_verified());
961
962 let carol_unsigned_device = machine
963 .get_device(DataSet::carol_id(), DataSet::carol_unsigned_device_id(), None)
964 .await
965 .unwrap()
966 .unwrap();
967 assert!(!carol_unsigned_device.is_verified());
968
969 let encryption_settings = error_on_verification_problem_encryption_settings();
971 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
972 let share_result = collect_session_recipients(
973 machine.store(),
974 vec![DataSet::bob_id(), DataSet::carol_id()].into_iter(),
975 &encryption_settings,
976 &group_session,
977 )
978 .await;
979
980 assert_let!(
981 Err(OlmError::SessionRecipientCollectionError(
982 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
983 )) = share_result
984 );
985
986 assert_eq!(
988 unverified_devices,
989 BTreeMap::from([
990 (DataSet::bob_id().to_owned(), vec![DataSet::bob_device_2_id().to_owned()]),
991 (
992 DataSet::carol_id().to_owned(),
993 vec![DataSet::carol_unsigned_device_id().to_owned()]
994 ),
995 ])
996 );
997 }
998
999 #[async_test]
1003 async fn test_error_on_unsigned_of_verified_resolve_by_whitelisting() {
1004 use VerificationViolationTestData as DataSet;
1005
1006 let machine = unsigned_of_verified_setup().await;
1007
1008 machine
1010 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1011 .await
1012 .unwrap()
1013 .unwrap()
1014 .set_local_trust(LocalTrust::Ignored)
1015 .await
1016 .unwrap();
1017
1018 let encryption_settings = error_on_verification_problem_encryption_settings();
1019 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1020
1021 let share_result = collect_session_recipients(
1023 machine.store(),
1024 iter::once(DataSet::bob_id()),
1025 &encryption_settings,
1026 &group_session,
1027 )
1028 .await
1029 .unwrap();
1030
1031 assert_eq!(2, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1032 assert_eq!(0, share_result.withheld_devices.len());
1033 }
1034
1035 #[async_test]
1039 async fn test_error_on_unsigned_of_verified_resolve_by_blacklisting() {
1040 use VerificationViolationTestData as DataSet;
1041
1042 let machine = unsigned_of_verified_setup().await;
1043
1044 machine
1046 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1047 .await
1048 .unwrap()
1049 .unwrap()
1050 .set_local_trust(LocalTrust::BlackListed)
1051 .await
1052 .unwrap();
1053
1054 let encryption_settings = error_on_verification_problem_encryption_settings();
1055 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1056
1057 let share_result = collect_session_recipients(
1059 machine.store(),
1060 iter::once(DataSet::bob_id()),
1061 &encryption_settings,
1062 &group_session,
1063 )
1064 .await
1065 .unwrap();
1066
1067 assert_eq!(1, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1068 let withheld_list: Vec<_> = share_result
1069 .withheld_devices
1070 .iter()
1071 .map(|(d, code)| (d.device_id().to_owned(), code.clone()))
1072 .collect();
1073 assert_eq!(
1074 withheld_list,
1075 vec![(DataSet::bob_device_2_id().to_owned(), WithheldCode::Blacklisted)]
1076 );
1077 }
1078
1079 #[async_test]
1083 async fn test_error_on_unsigned_of_verified_owner_is_us() {
1084 use VerificationViolationTestData as DataSet;
1085
1086 let machine = unsigned_of_verified_setup().await;
1087
1088 let mut own_keys = DataSet::own_keys_query_response_1().clone();
1090 own_keys.device_keys.insert(
1091 DataSet::own_id().to_owned(),
1092 BTreeMap::from([
1093 DataSet::own_signed_device_keys(),
1094 DataSet::own_unsigned_device_keys(),
1095 ]),
1096 );
1097 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1098
1099 let encryption_settings = error_on_verification_problem_encryption_settings();
1100 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1101 let share_result = collect_session_recipients(
1102 machine.store(),
1103 iter::once(DataSet::own_id()),
1104 &encryption_settings,
1105 &group_session,
1106 )
1107 .await;
1108
1109 assert_let!(
1110 Err(OlmError::SessionRecipientCollectionError(
1111 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
1112 )) = share_result
1113 );
1114
1115 assert_eq!(
1117 unverified_devices,
1118 BTreeMap::from([(
1119 DataSet::own_id().to_owned(),
1120 vec![DataSet::own_unsigned_device_id()]
1121 ),])
1122 );
1123 }
1124
1125 #[async_test]
1128 async fn test_should_not_error_on_unsigned_of_unverified() {
1129 use VerificationViolationTestData as DataSet;
1130
1131 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1132
1133 let own_keys = DataSet::own_keys_query_response_1();
1135 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1136
1137 machine
1139 .import_cross_signing_keys(CrossSigningKeyExport {
1140 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1141 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1142 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1143 })
1144 .await
1145 .unwrap();
1146
1147 let bob_keys = DataSet::bob_keys_query_response_rotated();
1149 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1150
1151 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1154 assert!(!bob_identity.other().unwrap().is_verified());
1155
1156 let bob_unsigned_device = machine
1157 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
1158 .await
1159 .unwrap()
1160 .unwrap();
1161 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1162
1163 let encryption_settings = error_on_verification_problem_encryption_settings();
1164 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1165 collect_session_recipients(
1166 machine.store(),
1167 iter::once(DataSet::bob_id()),
1168 &encryption_settings,
1169 &group_session,
1170 )
1171 .await
1172 .unwrap();
1173 }
1174
1175 #[async_test]
1178 async fn test_should_not_error_on_unsigned_of_signed_but_unverified() {
1179 use VerificationViolationTestData as DataSet;
1180
1181 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1182
1183 let keys_query = DataSet::own_keys_query_response_1();
1185 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1186
1187 let keys_query = DataSet::bob_keys_query_response_signed();
1189 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1190
1191 let bob_identity =
1194 machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap().other().unwrap();
1195 assert!(bob_identity
1196 .own_identity
1197 .as_ref()
1198 .unwrap()
1199 .is_identity_signed(&bob_identity.inner));
1200 assert!(!bob_identity.is_verified());
1201
1202 let bob_unsigned_device = machine
1203 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1204 .await
1205 .unwrap()
1206 .unwrap();
1207 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1208
1209 let encryption_settings = error_on_verification_problem_encryption_settings();
1211 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1212 collect_session_recipients(
1213 machine.store(),
1214 iter::once(DataSet::bob_id()),
1215 &encryption_settings,
1216 &group_session,
1217 )
1218 .await
1219 .unwrap();
1220 }
1221
1222 #[async_test]
1226 async fn test_verified_user_changed_identity() {
1227 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1228
1229 let machine = unsigned_of_verified_setup().await;
1232
1233 let bob_keys = DataSet::bob_keys_query_response_rotated();
1235 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1236
1237 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1239 assert!(bob_identity.has_verification_violation());
1240
1241 let encryption_settings = error_on_verification_problem_encryption_settings();
1243 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1244 let share_result = collect_session_recipients(
1245 machine.store(),
1246 iter::once(DataSet::bob_id()),
1247 &encryption_settings,
1248 &group_session,
1249 )
1250 .await;
1251
1252 assert_let!(
1253 Err(OlmError::SessionRecipientCollectionError(
1254 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1255 )) = share_result
1256 );
1257 assert_eq!(violating_users, vec![DataSet::bob_id()]);
1258
1259 bob_identity.withdraw_verification().await.unwrap();
1261
1262 collect_session_recipients(
1263 machine.store(),
1264 iter::once(DataSet::bob_id()),
1265 &encryption_settings,
1266 &group_session,
1267 )
1268 .await
1269 .unwrap();
1270 }
1271
1272 #[async_test]
1276 async fn test_own_verified_identity_changed() {
1277 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1278
1279 let machine = unsigned_of_verified_setup().await;
1281 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1282 assert!(own_identity.own().unwrap().is_verified());
1283
1284 let own_keys = DataSet::own_keys_query_response_2();
1286 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1287
1288 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1289 assert!(!own_identity.is_verified());
1290
1291 let encryption_settings = error_on_verification_problem_encryption_settings();
1293 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1294 let share_result = collect_session_recipients(
1295 machine.store(),
1296 iter::once(DataSet::own_id()),
1297 &encryption_settings,
1298 &group_session,
1299 )
1300 .await;
1301
1302 assert_let!(
1303 Err(OlmError::SessionRecipientCollectionError(
1304 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1305 )) = share_result
1306 );
1307 assert_eq!(violating_users, vec![DataSet::own_id()]);
1308
1309 own_identity.withdraw_verification().await.unwrap();
1311
1312 collect_session_recipients(
1313 machine.store(),
1314 iter::once(DataSet::own_id()),
1315 &encryption_settings,
1316 &group_session,
1317 )
1318 .await
1319 .unwrap();
1320 }
1321
1322 mod dehydrated_device {
1325 use std::{collections::HashSet, iter};
1326
1327 use insta::{allow_duplicates, assert_json_snapshot, with_settings};
1328 use matrix_sdk_common::deserialized_responses::WithheldCode;
1329 use matrix_sdk_test::{
1330 async_test, ruma_response_to_json,
1331 test_json::keys_query_sets::{
1332 KeyDistributionTestData, KeyQueryResponseTemplate,
1333 KeyQueryResponseTemplateDeviceOptions,
1334 },
1335 };
1336 use ruma::{device_id, user_id, DeviceId, TransactionId, UserId};
1337 use vodozemac::{Curve25519PublicKey, Ed25519SecretKey};
1338
1339 use super::{
1340 all_devices_strategy_settings, create_test_outbound_group_session,
1341 error_on_verification_problem_encryption_settings, identity_based_strategy_settings,
1342 test_machine,
1343 };
1344 use crate::{
1345 session_manager::group_sessions::{
1346 share_strategy::collect_session_recipients, CollectRecipientsResult,
1347 },
1348 EncryptionSettings, OlmMachine,
1349 };
1350
1351 #[async_test]
1352 async fn test_all_devices_strategy_should_share_with_verified_dehydrated_device() {
1353 should_share_with_verified_dehydrated_device(&all_devices_strategy_settings()).await
1354 }
1355
1356 #[async_test]
1357 async fn test_error_on_verification_problem_strategy_should_share_with_verified_dehydrated_device(
1358 ) {
1359 should_share_with_verified_dehydrated_device(
1360 &error_on_verification_problem_encryption_settings(),
1361 )
1362 .await
1363 }
1364
1365 #[async_test]
1366 async fn test_identity_based_strategy_should_share_with_verified_dehydrated_device() {
1367 should_share_with_verified_dehydrated_device(&identity_based_strategy_settings()).await
1368 }
1369
1370 async fn should_share_with_verified_dehydrated_device(
1375 encryption_settings: &EncryptionSettings,
1376 ) {
1377 let machine = test_machine().await;
1378
1379 let bob_user_id = user_id!("@bob:localhost");
1382 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1383 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1384 .with_dehydrated_device(bob_dehydrated_device_id, true)
1385 .build_response();
1386 allow_duplicates! {
1387 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1388 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1389 });
1390 }
1391 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1392
1393 let recips = share_test_session_and_collect_recipients(
1395 &machine,
1396 bob_user_id,
1397 encryption_settings,
1398 )
1399 .await;
1400
1401 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1403 }
1404
1405 #[async_test]
1406 async fn test_all_devices_strategy_should_not_share_with_unverified_dehydrated_device() {
1407 should_not_share_with_unverified_dehydrated_device(&all_devices_strategy_settings())
1408 .await
1409 }
1410
1411 #[async_test]
1412 async fn test_error_on_verification_problem_strategy_should_not_share_with_unverified_dehydrated_device(
1413 ) {
1414 should_not_share_with_unverified_dehydrated_device(
1415 &error_on_verification_problem_encryption_settings(),
1416 )
1417 .await
1418 }
1419
1420 #[async_test]
1421 async fn test_identity_based_strategy_should_not_share_with_unverified_dehydrated_device() {
1422 should_not_share_with_unverified_dehydrated_device(&identity_based_strategy_settings())
1423 .await
1424 }
1425
1426 async fn should_not_share_with_unverified_dehydrated_device(
1431 encryption_settings: &EncryptionSettings,
1432 ) {
1433 let machine = test_machine().await;
1434
1435 let bob_user_id = user_id!("@bob:localhost");
1438 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1439 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1440 .with_dehydrated_device(bob_dehydrated_device_id, false)
1441 .build_response();
1442 allow_duplicates! {
1443 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1444 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1445 });
1446 }
1447 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1448
1449 let recips = share_test_session_and_collect_recipients(
1451 &machine,
1452 bob_user_id,
1453 encryption_settings,
1454 )
1455 .await;
1456
1457 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1460 }
1461
1462 #[async_test]
1463 async fn test_all_devices_strategy_should_share_with_verified_device_of_pin_violation_user()
1464 {
1465 should_share_with_verified_device_of_pin_violation_user(
1466 &all_devices_strategy_settings(),
1467 )
1468 .await
1469 }
1470
1471 #[async_test]
1472 async fn test_error_on_verification_problem_strategy_should_share_with_verified_device_of_pin_violation_user(
1473 ) {
1474 should_share_with_verified_device_of_pin_violation_user(
1475 &error_on_verification_problem_encryption_settings(),
1476 )
1477 .await
1478 }
1479
1480 #[async_test]
1481 async fn test_identity_based_strategy_should_share_with_verified_device_of_pin_violation_user(
1482 ) {
1483 should_share_with_verified_device_of_pin_violation_user(
1484 &identity_based_strategy_settings(),
1485 )
1486 .await
1487 }
1488
1489 async fn should_share_with_verified_device_of_pin_violation_user(
1494 encryption_settings: &EncryptionSettings,
1495 ) {
1496 let machine = test_machine().await;
1497
1498 let bob_user_id = user_id!("@bob:localhost");
1500 let keys_query =
1501 key_query_response_template_with_cross_signing(bob_user_id).build_response();
1502 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1503
1504 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1507 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1508 .with_dehydrated_device(bob_dehydrated_device_id, true)
1509 .build_response();
1510 allow_duplicates! {
1511 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1512 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1513 });
1514 }
1515 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1516
1517 let recips = share_test_session_and_collect_recipients(
1519 &machine,
1520 bob_user_id,
1521 encryption_settings,
1522 )
1523 .await;
1524
1525 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1527 }
1528
1529 #[async_test]
1530 async fn test_all_devices_strategy_should_not_share_with_dehydrated_device_of_verification_violation_user(
1531 ) {
1532 should_not_share_with_dehydrated_device_of_verification_violation_user(
1533 &all_devices_strategy_settings(),
1534 )
1535 .await
1536 }
1537
1538 async fn should_not_share_with_dehydrated_device_of_verification_violation_user(
1541 encryption_settings: &EncryptionSettings,
1542 ) {
1543 let bob_user_id = user_id!("@bob:localhost");
1544 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1545 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1546 bob_user_id,
1547 bob_dehydrated_device_id,
1548 )
1549 .await;
1550
1551 let recips = share_test_session_and_collect_recipients(
1553 &machine,
1554 bob_user_id,
1555 encryption_settings,
1556 )
1557 .await;
1558
1559 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1562 }
1563
1564 #[async_test]
1565 async fn test_error_on_verification_problem_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1566 ) {
1567 should_give_error_for_dehydrated_device_of_verification_violation_user(
1568 &error_on_verification_problem_encryption_settings(),
1569 )
1570 .await
1571 }
1572
1573 #[async_test]
1574 async fn test_identity_based_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1575 ) {
1576 should_give_error_for_dehydrated_device_of_verification_violation_user(
1580 &identity_based_strategy_settings(),
1581 )
1582 .await
1583 }
1584
1585 async fn should_give_error_for_dehydrated_device_of_verification_violation_user(
1589 encryption_settings: &EncryptionSettings,
1590 ) {
1591 let bob_user_id = user_id!("@bob:localhost");
1592 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1593 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1594 bob_user_id,
1595 bob_dehydrated_device_id,
1596 )
1597 .await;
1598
1599 let group_session = create_test_outbound_group_session(&machine, encryption_settings);
1600 let share_result = collect_session_recipients(
1601 machine.store(),
1602 iter::once(bob_user_id),
1603 encryption_settings,
1604 &group_session,
1605 )
1606 .await;
1607
1608 assert_matches::assert_matches!(
1611 share_result,
1612 Err(crate::OlmError::SessionRecipientCollectionError(
1613 crate::SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
1614 ))
1615 );
1616 }
1617
1618 async fn prepare_machine_with_dehydrated_device_of_verification_violation_user(
1622 bob_user_id: &UserId,
1623 bob_dehydrated_device_id: &DeviceId,
1624 ) -> OlmMachine {
1625 let machine = test_machine().await;
1626
1627 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1629 .with_user_verification_signature(
1630 KeyDistributionTestData::me_id(),
1631 &KeyDistributionTestData::me_private_user_signing_key(),
1632 )
1633 .build_response();
1634 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1635
1636 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1639 .with_dehydrated_device(bob_dehydrated_device_id, true)
1640 .build_response();
1641 allow_duplicates! {
1642 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1643 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1644 });
1645 }
1646 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1647
1648 machine
1649 }
1650
1651 async fn share_test_session_and_collect_recipients(
1654 machine: &OlmMachine,
1655 target_user_id: &UserId,
1656 encryption_settings: &EncryptionSettings,
1657 ) -> CollectRecipientsResult {
1658 let group_session = create_test_outbound_group_session(machine, encryption_settings);
1659 collect_session_recipients(
1660 machine.store(),
1661 iter::once(target_user_id),
1662 encryption_settings,
1663 &group_session,
1664 )
1665 .await
1666 .unwrap()
1667 }
1668
1669 fn assert_shared_with(
1672 recips: CollectRecipientsResult,
1673 user_id: &UserId,
1674 device_ids: HashSet<&DeviceId>,
1675 ) {
1676 let bob_devices_shared: HashSet<_> = recips
1677 .devices
1678 .get(user_id)
1679 .unwrap_or_else(|| panic!("session not shared with {user_id}"))
1680 .iter()
1681 .map(|d| d.device_id())
1682 .collect();
1683 assert_eq!(bob_devices_shared, device_ids);
1684
1685 assert!(recips.withheld_devices.is_empty(), "Unexpected withheld messages");
1686 }
1687
1688 fn assert_withheld_to(
1691 recips: CollectRecipientsResult,
1692 bob_user_id: &UserId,
1693 bob_dehydrated_device_id: &DeviceId,
1694 ) {
1695 for (user, device_list) in recips.devices {
1697 assert_eq!(device_list.len(), 0, "session unexpectedly shared with {user}");
1698 }
1699
1700 assert_eq!(recips.withheld_devices.len(), 1);
1702 assert_eq!(recips.withheld_devices[0].0.user_id(), bob_user_id);
1703 assert_eq!(recips.withheld_devices[0].0.device_id(), bob_dehydrated_device_id);
1704 assert_eq!(recips.withheld_devices[0].1, WithheldCode::Unverified);
1705 }
1706
1707 fn key_query_response_template_with_cross_signing(
1710 user_id: &UserId,
1711 ) -> KeyQueryResponseTemplate {
1712 KeyQueryResponseTemplate::new(user_id.to_owned()).with_cross_signing_keys(
1713 Ed25519SecretKey::from_slice(b"master12master12master12master12"),
1714 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1715 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1716 )
1717 }
1718
1719 fn key_query_response_template_with_changed_cross_signing(
1723 bob_user_id: &UserId,
1724 ) -> KeyQueryResponseTemplate {
1725 KeyQueryResponseTemplate::new(bob_user_id.to_owned()).with_cross_signing_keys(
1726 Ed25519SecretKey::from_slice(b"newmaster__newmaster__newmaster_"),
1727 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1728 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1729 )
1730 }
1731
1732 trait KeyQueryResponseTemplateExt {
1733 fn with_dehydrated_device(
1734 self,
1735 device_id: &DeviceId,
1736 verified: bool,
1737 ) -> KeyQueryResponseTemplate;
1738 }
1739
1740 impl KeyQueryResponseTemplateExt for KeyQueryResponseTemplate {
1741 fn with_dehydrated_device(
1743 self,
1744 device_id: &DeviceId,
1745 verified: bool,
1746 ) -> KeyQueryResponseTemplate {
1747 self.with_device(
1748 device_id,
1749 &Curve25519PublicKey::from(b"curvepubcurvepubcurvepubcurvepub".to_owned()),
1750 &Ed25519SecretKey::from_slice(b"device12device12device12device12"),
1751 KeyQueryResponseTemplateDeviceOptions::new()
1752 .dehydrated(true)
1753 .verified(verified),
1754 )
1755 }
1756 }
1757 }
1758
1759 #[async_test]
1760 async fn test_share_with_identity_strategy() {
1761 let machine = test_machine().await;
1762 import_known_users_to_test_machine(&machine).await;
1763
1764 let encryption_settings = identity_based_strategy_settings();
1765
1766 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1767
1768 let share_result = collect_session_recipients(
1769 machine.store(),
1770 vec![
1771 KeyDistributionTestData::dan_id(),
1772 KeyDistributionTestData::dave_id(),
1773 KeyDistributionTestData::good_id(),
1774 ]
1775 .into_iter(),
1776 &encryption_settings,
1777 &group_session,
1778 )
1779 .await
1780 .unwrap();
1781
1782 assert!(!share_result.should_rotate);
1783
1784 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
1785 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
1786 assert!(dave_devices_shared.unwrap().is_empty());
1788
1789 assert_eq!(good_devices_shared.unwrap().len(), 2);
1791
1792 let dan_devices_shared =
1795 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
1796
1797 assert_eq!(dan_devices_shared.len(), 1);
1798 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
1799 assert_eq!(
1800 dan_device_that_will_get_the_key.device_id().as_str(),
1801 KeyDistributionTestData::dan_signed_device_id()
1802 );
1803
1804 let (_, code) = share_result
1806 .withheld_devices
1807 .iter()
1808 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
1809 .expect("This dan's device should receive a withheld code");
1810
1811 assert_eq!(code, &WithheldCode::Unverified);
1812
1813 let (_, code) = share_result
1815 .withheld_devices
1816 .iter()
1817 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
1818 .expect("This dave device should receive a withheld code");
1819
1820 assert_eq!(code, &WithheldCode::Unverified);
1821 }
1822
1823 #[async_test]
1826 async fn test_share_identity_strategy_no_cross_signing() {
1827 let machine: OlmMachine = OlmMachine::new(
1830 KeyDistributionTestData::me_id(),
1831 KeyDistributionTestData::me_device_id(),
1832 )
1833 .await;
1834
1835 let keys_query = KeyDistributionTestData::dan_keys_query_response();
1836 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1837
1838 let fake_room_id = room_id!("!roomid:localhost");
1839
1840 let encryption_settings = identity_based_strategy_settings();
1841
1842 let request_result = machine
1843 .share_room_key(
1844 fake_room_id,
1845 iter::once(KeyDistributionTestData::dan_id()),
1846 encryption_settings.clone(),
1847 )
1848 .await;
1849
1850 assert_matches!(
1851 request_result,
1852 Err(OlmError::SessionRecipientCollectionError(
1853 SessionRecipientCollectionError::CrossSigningNotSetup
1854 ))
1855 );
1856
1857 let keys_query = KeyDistributionTestData::me_keys_query_response();
1861 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1862
1863 let request_result = machine
1864 .share_room_key(
1865 fake_room_id,
1866 iter::once(KeyDistributionTestData::dan_id()),
1867 encryption_settings.clone(),
1868 )
1869 .await;
1870
1871 assert_matches!(
1872 request_result,
1873 Err(OlmError::SessionRecipientCollectionError(
1874 SessionRecipientCollectionError::SendingFromUnverifiedDevice
1875 ))
1876 );
1877
1878 machine
1881 .import_cross_signing_keys(CrossSigningKeyExport {
1882 master_key: KeyDistributionTestData::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1883 self_signing_key: KeyDistributionTestData::SELF_SIGNING_KEY_PRIVATE_EXPORT
1884 .to_owned()
1885 .into(),
1886 user_signing_key: KeyDistributionTestData::USER_SIGNING_KEY_PRIVATE_EXPORT
1887 .to_owned()
1888 .into(),
1889 })
1890 .await
1891 .unwrap();
1892
1893 let requests = machine
1894 .share_room_key(
1895 fake_room_id,
1896 iter::once(KeyDistributionTestData::dan_id()),
1897 encryption_settings.clone(),
1898 )
1899 .await
1900 .unwrap();
1901
1902 assert_eq!(requests.len(), 1);
1905 }
1906
1907 #[async_test]
1911 async fn test_share_identity_strategy_report_verification_violation() {
1912 let machine: OlmMachine = OlmMachine::new(
1913 KeyDistributionTestData::me_id(),
1914 KeyDistributionTestData::me_device_id(),
1915 )
1916 .await;
1917
1918 machine.bootstrap_cross_signing(false).await.unwrap();
1919
1920 let user1 = IdentityChangeDataSet::user_id();
1922 let user2 = MaloIdentityChangeDataSet::user_id();
1923
1924 let keys_query = IdentityChangeDataSet::key_query_with_identity_a();
1926 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1927
1928 let keys_query = MaloIdentityChangeDataSet::initial_key_query();
1929 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1930
1931 let keys_query = IdentityChangeDataSet::key_query_with_identity_b();
1935 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1936 machine
1937 .get_identity(user1, None)
1938 .await
1939 .unwrap()
1940 .unwrap()
1941 .other()
1942 .unwrap()
1943 .mark_as_previously_verified()
1944 .await
1945 .unwrap();
1946
1947 let keys_query = MaloIdentityChangeDataSet::updated_key_query();
1948 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1949 machine
1950 .get_identity(user2, None)
1951 .await
1952 .unwrap()
1953 .unwrap()
1954 .other()
1955 .unwrap()
1956 .mark_as_previously_verified()
1957 .await
1958 .unwrap();
1959
1960 let fake_room_id = room_id!("!roomid:localhost");
1961
1962 let encryption_settings = identity_based_strategy_settings();
1964
1965 let request_result = machine
1966 .share_room_key(
1967 fake_room_id,
1968 vec![user1, user2].into_iter(),
1969 encryption_settings.clone(),
1970 )
1971 .await;
1972
1973 assert_let!(
1976 Err(OlmError::SessionRecipientCollectionError(
1977 SessionRecipientCollectionError::VerifiedUserChangedIdentity(affected_users)
1978 )) = request_result
1979 );
1980 assert_eq!(2, affected_users.len());
1982
1983 machine
1985 .get_identity(user1, None)
1986 .await
1987 .unwrap()
1988 .unwrap()
1989 .withdraw_verification()
1990 .await
1991 .unwrap();
1992
1993 let verification_request = machine
1995 .get_identity(user2, None)
1996 .await
1997 .unwrap()
1998 .unwrap()
1999 .other()
2000 .unwrap()
2001 .verify()
2002 .await
2003 .unwrap();
2004
2005 let master_key =
2006 &machine.get_identity(user2, None).await.unwrap().unwrap().other().unwrap().master_key;
2007
2008 let my_identity = machine
2009 .get_identity(KeyDistributionTestData::me_id(), None)
2010 .await
2011 .expect("Should not fail to find own identity")
2012 .expect("Our own identity should not be missing")
2013 .own()
2014 .expect("Our own identity should be of type Own");
2015
2016 let msk = json!({ user2: serde_json::to_value(master_key).expect("Should not fail to serialize")});
2017 let ssk =
2018 serde_json::to_value(&MaloIdentityChangeDataSet::updated_key_query().self_signing_keys)
2019 .expect("Should not fail to serialize");
2020
2021 let kq_response = simulate_key_query_response_for_verification(
2022 verification_request,
2023 my_identity,
2024 KeyDistributionTestData::me_id(),
2025 user2,
2026 msk,
2027 ssk,
2028 );
2029
2030 machine
2031 .mark_request_as_sent(
2032 &TransactionId::new(),
2033 crate::types::requests::AnyIncomingResponse::KeysQuery(&kq_response),
2034 )
2035 .await
2036 .unwrap();
2037
2038 assert!(machine.get_identity(user2, None).await.unwrap().unwrap().is_verified());
2039
2040 machine
2042 .share_room_key(
2043 fake_room_id,
2044 vec![user1, user2].into_iter(),
2045 encryption_settings.clone(),
2046 )
2047 .await
2048 .unwrap();
2049 }
2050
2051 #[async_test]
2052 async fn test_should_rotate_based_on_visibility() {
2053 let machine = test_machine().await;
2054 import_known_users_to_test_machine(&machine).await;
2055
2056 let strategy = CollectStrategy::AllDevices;
2057
2058 let encryption_settings = EncryptionSettings {
2059 sharing_strategy: strategy.clone(),
2060 history_visibility: HistoryVisibility::Invited,
2061 ..Default::default()
2062 };
2063
2064 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2065
2066 let _ = collect_session_recipients(
2067 machine.store(),
2068 vec![KeyDistributionTestData::dan_id()].into_iter(),
2069 &encryption_settings,
2070 &group_session,
2071 )
2072 .await
2073 .unwrap();
2074
2075 let encryption_settings = EncryptionSettings {
2077 sharing_strategy: strategy.clone(),
2078 history_visibility: HistoryVisibility::Shared,
2079 ..Default::default()
2080 };
2081
2082 let share_result = collect_session_recipients(
2083 machine.store(),
2084 vec![KeyDistributionTestData::dan_id()].into_iter(),
2085 &encryption_settings,
2086 &group_session,
2087 )
2088 .await
2089 .unwrap();
2090
2091 assert!(share_result.should_rotate);
2092 }
2093
2094 #[async_test]
2098 async fn test_should_rotate_based_on_device_excluded() {
2099 let machine = test_machine().await;
2100 import_known_users_to_test_machine(&machine).await;
2101
2102 let encryption_settings = all_devices_strategy_settings();
2103 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2104 let sender_key = machine.identity_keys().curve25519;
2105
2106 group_session
2107 .mark_shared_with(
2108 KeyDistributionTestData::dan_id(),
2109 KeyDistributionTestData::dan_signed_device_id(),
2110 sender_key,
2111 )
2112 .await;
2113 group_session
2114 .mark_shared_with(
2115 KeyDistributionTestData::dan_id(),
2116 KeyDistributionTestData::dan_unsigned_device_id(),
2117 sender_key,
2118 )
2119 .await;
2120
2121 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2123 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2124
2125 let share_result = collect_session_recipients(
2127 machine.store(),
2128 vec![KeyDistributionTestData::dan_id()].into_iter(),
2129 &encryption_settings,
2130 &group_session,
2131 )
2132 .await
2133 .unwrap();
2134
2135 assert!(share_result.should_rotate);
2136 }
2137
2138 #[async_test]
2141 async fn test_should_rotate_based_on_device_with_pending_request_excluded() {
2142 let machine = test_machine().await;
2143 import_known_users_to_test_machine(&machine).await;
2144
2145 let encryption_settings = all_devices_strategy_settings();
2146 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2147 let sender_key = machine.identity_keys().curve25519;
2148
2149 let dan_user = KeyDistributionTestData::dan_id();
2150 let dan_dev1 = KeyDistributionTestData::dan_signed_device_id();
2151 let dan_dev2 = KeyDistributionTestData::dan_unsigned_device_id();
2152
2153 group_session.mark_shared_with(dan_user, dan_dev1, sender_key).await;
2155
2156 {
2157 let share_infos = BTreeMap::from([(
2159 dan_user.to_owned(),
2160 BTreeMap::from([(
2161 dan_dev2.to_owned(),
2162 ShareInfo::new_shared(sender_key, 0, SequenceNumber::default()),
2163 )]),
2164 )]);
2165
2166 let txid = TransactionId::new();
2167 let req = Arc::new(ToDeviceRequest::for_recipients(
2168 dan_user,
2169 vec![dan_dev2.to_owned()],
2170 &ruma::events::AnyToDeviceEventContent::Dummy(ToDeviceDummyEventContent),
2171 txid.clone(),
2172 ));
2173 group_session.add_request(txid, req, share_infos);
2174 }
2175
2176 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2178 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2179
2180 let share_result = collect_session_recipients(
2182 machine.store(),
2183 vec![KeyDistributionTestData::dan_id()].into_iter(),
2184 &encryption_settings,
2185 &group_session,
2186 )
2187 .await
2188 .unwrap();
2189
2190 assert!(share_result.should_rotate);
2191 }
2192
2193 #[async_test]
2196 async fn test_should_not_rotate_if_keys_were_withheld() {
2197 let machine = test_machine().await;
2198 import_known_users_to_test_machine(&machine).await;
2199
2200 let encryption_settings = all_devices_strategy_settings();
2201 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2202 let fake_room_id = group_session.room_id();
2203
2204 let requests = machine
2207 .share_room_key(
2208 fake_room_id,
2209 vec![KeyDistributionTestData::dan_id()].into_iter(),
2210 encryption_settings.clone(),
2211 )
2212 .await
2213 .unwrap();
2214
2215 for r in requests {
2216 machine
2217 .inner
2218 .group_session_manager
2219 .mark_request_as_sent(r.as_ref().txn_id.as_ref())
2220 .await
2221 .unwrap();
2222 }
2223
2224 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2226 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2227
2228 let share_result = collect_session_recipients(
2230 machine.store(),
2231 vec![KeyDistributionTestData::dan_id()].into_iter(),
2232 &encryption_settings,
2233 &group_session,
2234 )
2235 .await
2236 .unwrap();
2237
2238 assert!(!share_result.should_rotate);
2239 }
2240
2241 async fn unsigned_of_verified_setup() -> OlmMachine {
2249 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
2250
2251 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
2252
2253 let own_keys = DataSet::own_keys_query_response_1();
2255 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
2256
2257 machine
2259 .import_cross_signing_keys(CrossSigningKeyExport {
2260 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
2261 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2262 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2263 })
2264 .await
2265 .unwrap();
2266
2267 let bob_keys = DataSet::bob_keys_query_response_signed();
2269 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
2270
2271 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
2274 assert!(bob_identity.other().unwrap().is_verified());
2275
2276 let bob_signed_device = machine
2277 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
2278 .await
2279 .unwrap()
2280 .unwrap();
2281 assert!(bob_signed_device.is_verified());
2282 assert!(bob_signed_device.device_owner_identity.is_some());
2283
2284 let bob_unsigned_device = machine
2285 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
2286 .await
2287 .unwrap()
2288 .unwrap();
2289 assert!(!bob_unsigned_device.is_verified());
2290
2291 machine
2292 }
2293
2294 fn all_devices_strategy_settings() -> EncryptionSettings {
2296 EncryptionSettings { sharing_strategy: CollectStrategy::AllDevices, ..Default::default() }
2297 }
2298
2299 fn error_on_verification_problem_encryption_settings() -> EncryptionSettings {
2302 EncryptionSettings {
2303 sharing_strategy: CollectStrategy::ErrorOnVerifiedUserProblem,
2304 ..Default::default()
2305 }
2306 }
2307
2308 fn identity_based_strategy_settings() -> EncryptionSettings {
2310 EncryptionSettings {
2311 sharing_strategy: CollectStrategy::IdentityBasedStrategy,
2312 ..Default::default()
2313 }
2314 }
2315
2316 fn create_test_outbound_group_session(
2319 machine: &OlmMachine,
2320 encryption_settings: &EncryptionSettings,
2321 ) -> OutboundGroupSession {
2322 OutboundGroupSession::new(
2323 machine.device_id().into(),
2324 Arc::new(machine.identity_keys()),
2325 room_id!("!roomid:localhost"),
2326 encryption_settings.clone(),
2327 )
2328 .expect("creating an outbound group session should not fail")
2329 }
2330}