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!(
234 "CollectStrategy::AllDevices: Considering recipient devices for user {}",
235 user_id
236 );
237 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
238 let device_owner_identity = store.get_user_identity(user_id).await?;
239
240 let recipient_devices = split_devices_for_user_for_all_devices_strategy(
241 user_devices,
242 &own_identity,
243 &device_owner_identity,
244 );
245 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
246 }
247 }
248 CollectStrategy::ErrorOnVerifiedUserProblem => {
249 let mut unsigned_devices_of_verified_users: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>> =
250 Default::default();
251
252 for user_id in users {
253 trace!("CollectStrategy::ErrorOnVerifiedUserProblem: Considering recipient devices for user {}", user_id);
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!("CollectStrategy::IdentityBasedStrategy: Considering recipient devices for user {}", user_id);
319 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
320
321 let device_owner_identity = store.get_user_identity(user_id).await?;
322
323 if has_identity_verification_violation(
324 own_identity.as_ref(),
325 device_owner_identity.as_ref(),
326 ) {
327 verified_users_with_new_identities.push(user_id.to_owned());
328 continue;
330 }
331
332 let recipient_devices = split_devices_for_user_for_identity_based_strategy(
333 user_devices,
334 &device_owner_identity,
335 );
336
337 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
338 }
339 }
340
341 CollectStrategy::OnlyTrustedDevices => {
342 for user_id in users {
343 trace!("CollectStrategy::OnlyTrustedDevices: Considering recipient devices for user {}", user_id);
344 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
345 let device_owner_identity = store.get_user_identity(user_id).await?;
346
347 let recipient_devices = split_devices_for_user_for_only_trusted_devices(
348 user_devices,
349 &own_identity,
350 &device_owner_identity,
351 );
352
353 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
354 }
355 }
356 }
357
358 if !verified_users_with_new_identities.is_empty() {
361 return Err(OlmError::SessionRecipientCollectionError(
362 SessionRecipientCollectionError::VerifiedUserChangedIdentity(
363 verified_users_with_new_identities,
364 ),
365 ));
366 }
367
368 trace!(result.should_rotate, "Done calculating group session recipients");
369
370 Ok(result)
371}
372
373fn update_recipients_for_user(
376 recipients: &mut CollectRecipientsResult,
377 outbound: Option<&OutboundGroupSession>,
378 user_id: &UserId,
379 recipient_devices: RecipientDevicesForUser,
380) {
381 if let Some(outbound) = outbound {
386 if !recipients.should_rotate {
387 recipients.should_rotate = is_session_overshared_for_user(
388 outbound,
389 user_id,
390 &recipient_devices.allowed_devices,
391 )
392 }
393 }
394
395 recipients
396 .devices
397 .entry(user_id.to_owned())
398 .or_default()
399 .extend(recipient_devices.allowed_devices);
400 recipients.withheld_devices.extend(recipient_devices.denied_devices_with_code);
401}
402
403fn is_session_overshared_for_user(
419 outbound_session: &OutboundGroupSession,
420 user_id: &UserId,
421 recipient_devices: &[DeviceData],
422) -> bool {
423 let recipient_device_ids: BTreeSet<&DeviceId> =
425 recipient_devices.iter().map(|d| d.device_id()).collect();
426
427 let view = outbound_session.sharing_view();
428 let newly_deleted_or_blacklisted: BTreeSet<&DeviceId> = view
429 .iter_shares(Some(user_id), None)
430 .filter_map(|(_user_id, device_id, info)| {
431 if matches!(info, ShareInfo::Shared(_)) && !recipient_device_ids.contains(device_id) {
435 Some(device_id)
436 } else {
437 None
438 }
439 })
440 .collect();
441
442 let should_rotate = !newly_deleted_or_blacklisted.is_empty();
443 if should_rotate {
444 debug!(
445 "Rotating a room key due to these devices being deleted/blacklisted {:?}",
446 newly_deleted_or_blacklisted,
447 );
448 }
449 should_rotate
450}
451
452#[derive(Default)]
459struct RecipientDevicesForUser {
460 allowed_devices: Vec<DeviceData>,
462 denied_devices_with_code: Vec<(DeviceData, WithheldCode)>,
464}
465
466enum ErrorOnVerifiedUserProblemResult {
469 UnsignedDevicesOfVerifiedUser(Vec<OwnedDeviceId>),
473
474 Devices(RecipientDevicesForUser),
476}
477
478fn split_devices_for_user_for_all_devices_strategy(
481 user_devices: HashMap<OwnedDeviceId, DeviceData>,
482 own_identity: &Option<OwnUserIdentityData>,
483 device_owner_identity: &Option<UserIdentityData>,
484) -> RecipientDevicesForUser {
485 let (left, right) = user_devices.into_values().partition_map(|d| {
486 if d.is_blacklisted() {
487 Either::Right((d, WithheldCode::Blacklisted))
488 } else if d.is_dehydrated()
489 && should_withhold_to_dehydrated_device(
490 &d,
491 own_identity.as_ref(),
492 device_owner_identity.as_ref(),
493 )
494 {
495 Either::Right((d, WithheldCode::Unverified))
496 } else {
497 Either::Left(d)
498 }
499 });
500
501 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
502}
503
504fn should_withhold_to_dehydrated_device(
513 device: &DeviceData,
514 own_identity: Option<&OwnUserIdentityData>,
515 device_owner_identity: Option<&UserIdentityData>,
516) -> bool {
517 device_owner_identity.is_none_or(|owner_id| {
518 !device.is_cross_signed_by_owner(owner_id) ||
520
521 (owner_id.was_previously_verified() && !is_user_verified(own_identity, owner_id))
523 })
524}
525
526fn split_devices_for_user_for_error_on_verified_user_problem_strategy(
539 user_devices: HashMap<OwnedDeviceId, DeviceData>,
540 own_identity: &Option<OwnUserIdentityData>,
541 device_owner_identity: &Option<UserIdentityData>,
542) -> ErrorOnVerifiedUserProblemResult {
543 let mut recipient_devices = RecipientDevicesForUser::default();
544
545 let mut unsigned_devices_of_verified_users: Option<Vec<OwnedDeviceId>> = None;
548
549 for d in user_devices.into_values() {
550 match handle_device_for_user_for_error_on_verified_user_problem_strategy(
551 &d,
552 own_identity.as_ref(),
553 device_owner_identity.as_ref(),
554 ) {
555 ErrorOnVerifiedUserProblemDeviceDecision::Ok => {
556 recipient_devices.allowed_devices.push(d)
557 }
558 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => {
559 recipient_devices.denied_devices_with_code.push((d, code))
560 }
561 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
562 unsigned_devices_of_verified_users
563 .get_or_insert_with(Vec::default)
564 .push(d.device_id().to_owned())
565 }
566 }
567 }
568
569 if let Some(devices) = unsigned_devices_of_verified_users {
570 ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices)
571 } else {
572 ErrorOnVerifiedUserProblemResult::Devices(recipient_devices)
573 }
574}
575
576enum ErrorOnVerifiedUserProblemDeviceDecision {
579 Ok,
580 Withhold(WithheldCode),
581 UnsignedOfVerified,
582}
583
584fn handle_device_for_user_for_error_on_verified_user_problem_strategy(
585 device: &DeviceData,
586 own_identity: Option<&OwnUserIdentityData>,
587 device_owner_identity: Option<&UserIdentityData>,
588) -> ErrorOnVerifiedUserProblemDeviceDecision {
589 if device.is_blacklisted() {
590 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Blacklisted)
591 } else if device.local_trust_state() == LocalTrust::Ignored {
592 ErrorOnVerifiedUserProblemDeviceDecision::Ok
594 } else if is_unsigned_device_of_verified_user(own_identity, device_owner_identity, device) {
595 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified
596 } else if device.is_dehydrated()
597 && device_owner_identity.is_none_or(|owner_id| {
598 !device.is_cross_signed_by_owner(owner_id)
601 })
602 {
603 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Unverified)
604 } else {
605 ErrorOnVerifiedUserProblemDeviceDecision::Ok
606 }
607}
608
609fn split_devices_for_user_for_identity_based_strategy(
610 user_devices: HashMap<OwnedDeviceId, DeviceData>,
611 device_owner_identity: &Option<UserIdentityData>,
612) -> RecipientDevicesForUser {
613 match device_owner_identity {
614 None => {
615 RecipientDevicesForUser {
618 allowed_devices: Vec::default(),
619 denied_devices_with_code: user_devices
620 .into_values()
621 .map(|d| (d, WithheldCode::Unverified))
622 .collect(),
623 }
624 }
625 Some(device_owner_identity) => {
626 let (recipients, withheld_recipients): (
628 Vec<DeviceData>,
629 Vec<(DeviceData, WithheldCode)>,
630 ) = user_devices.into_values().partition_map(|d| {
631 if d.is_cross_signed_by_owner(device_owner_identity) {
632 Either::Left(d)
633 } else {
634 Either::Right((d, WithheldCode::Unverified))
635 }
636 });
637 RecipientDevicesForUser {
638 allowed_devices: recipients,
639 denied_devices_with_code: withheld_recipients,
640 }
641 }
642 }
643}
644
645fn split_devices_for_user_for_only_trusted_devices(
648 user_devices: HashMap<OwnedDeviceId, DeviceData>,
649 own_identity: &Option<OwnUserIdentityData>,
650 device_owner_identity: &Option<UserIdentityData>,
651) -> RecipientDevicesForUser {
652 let (left, right) = user_devices.into_values().partition_map(|d| {
653 match (
654 d.local_trust_state(),
655 d.is_cross_signing_trusted(own_identity, device_owner_identity),
656 ) {
657 (LocalTrust::BlackListed, _) => Either::Right((d, WithheldCode::Blacklisted)),
658 (LocalTrust::Ignored | LocalTrust::Verified, _) => Either::Left(d),
659 (LocalTrust::Unset, false) => Either::Right((d, WithheldCode::Unverified)),
660 (LocalTrust::Unset, true) => Either::Left(d),
661 }
662 });
663 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
664}
665
666fn is_unsigned_device_of_verified_user(
667 own_identity: Option<&OwnUserIdentityData>,
668 device_owner_identity: Option<&UserIdentityData>,
669 device_data: &DeviceData,
670) -> bool {
671 device_owner_identity.is_some_and(|device_owner_identity| {
672 is_user_verified(own_identity, device_owner_identity)
673 && !device_data.is_cross_signed_by_owner(device_owner_identity)
674 })
675}
676
677fn has_identity_verification_violation(
684 own_identity: Option<&OwnUserIdentityData>,
685 device_owner_identity: Option<&UserIdentityData>,
686) -> bool {
687 device_owner_identity.is_some_and(|device_owner_identity| {
688 device_owner_identity.was_previously_verified()
689 && !is_user_verified(own_identity, device_owner_identity)
690 })
691}
692
693fn is_user_verified(
694 own_identity: Option<&OwnUserIdentityData>,
695 user_identity: &UserIdentityData,
696) -> bool {
697 match user_identity {
698 UserIdentityData::Own(own_identity) => own_identity.is_verified(),
699 UserIdentityData::Other(other_identity) => {
700 own_identity.is_some_and(|oi| oi.is_identity_verified(other_identity))
701 }
702 }
703}
704
705#[cfg(test)]
706mod tests {
707 use std::{collections::BTreeMap, iter, sync::Arc};
708
709 use assert_matches::assert_matches;
710 use assert_matches2::assert_let;
711 use insta::{assert_snapshot, with_settings};
712 use matrix_sdk_common::deserialized_responses::WithheldCode;
713 use matrix_sdk_test::{
714 async_test, test_json,
715 test_json::keys_query_sets::{
716 IdentityChangeDataSet, KeyDistributionTestData, MaloIdentityChangeDataSet,
717 VerificationViolationTestData,
718 },
719 };
720 use ruma::{
721 device_id,
722 events::{dummy::ToDeviceDummyEventContent, room::history_visibility::HistoryVisibility},
723 room_id, TransactionId,
724 };
725 use serde_json::json;
726
727 use crate::{
728 error::SessionRecipientCollectionError,
729 olm::{OutboundGroupSession, ShareInfo},
730 session_manager::{
731 group_sessions::share_strategy::collect_session_recipients, CollectStrategy,
732 },
733 store::caches::SequenceNumber,
734 testing::simulate_key_query_response_for_verification,
735 types::requests::ToDeviceRequest,
736 CrossSigningKeyExport, EncryptionSettings, LocalTrust, OlmError, OlmMachine,
737 };
738
739 async fn test_machine() -> OlmMachine {
743 use KeyDistributionTestData as DataSet;
744
745 let machine = OlmMachine::new(DataSet::me_id(), DataSet::me_device_id()).await;
747 let keys_query = DataSet::me_keys_query_response();
748 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
749
750 machine
752 .import_cross_signing_keys(CrossSigningKeyExport {
753 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
754 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
755 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
756 })
757 .await
758 .unwrap();
759
760 machine
761 }
762
763 async fn import_known_users_to_test_machine(machine: &OlmMachine) {
766 let keys_query = KeyDistributionTestData::dan_keys_query_response();
767 let txn_id = TransactionId::new();
768 machine.mark_request_as_sent(&txn_id, &keys_query).await.unwrap();
769
770 let txn_id_dave = TransactionId::new();
771 let keys_query_dave = KeyDistributionTestData::dave_keys_query_response();
772 machine.mark_request_as_sent(&txn_id_dave, &keys_query_dave).await.unwrap();
773
774 let txn_id_good = TransactionId::new();
775 let keys_query_good = KeyDistributionTestData::good_keys_query_response();
776 machine.mark_request_as_sent(&txn_id_good, &keys_query_good).await.unwrap();
777 }
778
779 #[test]
782 fn test_serialize_device_based_strategy() {
783 let encryption_settings = all_devices_strategy_settings();
784 let serialized = serde_json::to_string(&encryption_settings).unwrap();
785 with_settings!({prepend_module_to_snapshot => false}, {
786 assert_snapshot!(serialized)
787 });
788 }
789
790 #[test]
794 fn test_deserialize_old_device_based_strategy() {
795 let settings: EncryptionSettings = serde_json::from_value(json!({
796 "algorithm": "m.megolm.v1.aes-sha2",
797 "rotation_period":{"secs":604800,"nanos":0},
798 "rotation_period_msgs":100,
799 "history_visibility":"shared",
800 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":false}},
801 })).unwrap();
802 assert_matches!(settings.sharing_strategy, CollectStrategy::AllDevices);
803 }
804
805 #[test]
809 fn test_deserialize_old_error_on_verified_user_problem() {
810 let settings: EncryptionSettings = serde_json::from_value(json!({
811 "algorithm": "m.megolm.v1.aes-sha2",
812 "rotation_period":{"secs":604800,"nanos":0},
813 "rotation_period_msgs":100,
814 "history_visibility":"shared",
815 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":true}},
816 })).unwrap();
817 assert_matches!(settings.sharing_strategy, CollectStrategy::ErrorOnVerifiedUserProblem);
818 }
819
820 #[test]
824 fn test_deserialize_old_only_trusted_devices_strategy() {
825 let settings: EncryptionSettings = serde_json::from_value(json!({
826 "algorithm": "m.megolm.v1.aes-sha2",
827 "rotation_period":{"secs":604800,"nanos":0},
828 "rotation_period_msgs":100,
829 "history_visibility":"shared",
830 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":true,"error_on_verified_user_problem":false}},
831 })).unwrap();
832 assert_matches!(settings.sharing_strategy, CollectStrategy::OnlyTrustedDevices);
833 }
834
835 #[async_test]
836 async fn test_share_with_per_device_strategy_to_all() {
837 let machine = test_machine().await;
838 import_known_users_to_test_machine(&machine).await;
839
840 let encryption_settings = all_devices_strategy_settings();
841
842 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
843
844 let share_result = collect_session_recipients(
845 machine.store(),
846 vec![
847 KeyDistributionTestData::dan_id(),
848 KeyDistributionTestData::dave_id(),
849 KeyDistributionTestData::good_id(),
850 ]
851 .into_iter(),
852 &encryption_settings,
853 &group_session,
854 )
855 .await
856 .unwrap();
857
858 assert!(!share_result.should_rotate);
859
860 let dan_devices_shared =
861 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
862 let dave_devices_shared =
863 share_result.devices.get(KeyDistributionTestData::dave_id()).unwrap();
864 let good_devices_shared =
865 share_result.devices.get(KeyDistributionTestData::good_id()).unwrap();
866
867 assert_eq!(dan_devices_shared.len(), 2);
869 assert_eq!(dave_devices_shared.len(), 1);
870 assert_eq!(good_devices_shared.len(), 2);
871 }
872
873 #[async_test]
874 async fn test_share_with_only_trusted_strategy() {
875 let machine = test_machine().await;
876 import_known_users_to_test_machine(&machine).await;
877
878 let encryption_settings = EncryptionSettings {
879 sharing_strategy: CollectStrategy::OnlyTrustedDevices,
880 ..Default::default()
881 };
882
883 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
884
885 let share_result = collect_session_recipients(
886 machine.store(),
887 vec![
888 KeyDistributionTestData::dan_id(),
889 KeyDistributionTestData::dave_id(),
890 KeyDistributionTestData::good_id(),
891 ]
892 .into_iter(),
893 &encryption_settings,
894 &group_session,
895 )
896 .await
897 .unwrap();
898
899 assert!(!share_result.should_rotate);
900
901 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
902 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
903 assert!(dave_devices_shared.unwrap().is_empty());
905 assert!(good_devices_shared.unwrap().is_empty());
906
907 let dan_devices_shared =
910 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
911
912 assert_eq!(dan_devices_shared.len(), 1);
913 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
914 assert_eq!(
915 dan_device_that_will_get_the_key.device_id().as_str(),
916 KeyDistributionTestData::dan_signed_device_id()
917 );
918
919 let (_, code) = share_result
921 .withheld_devices
922 .iter()
923 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
924 .expect("This dan's device should receive a withheld code");
925
926 assert_eq!(code, &WithheldCode::Unverified);
927
928 let (_, code) = share_result
929 .withheld_devices
930 .iter()
931 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
932 .expect("This daves's device should receive a withheld code");
933
934 assert_eq!(code, &WithheldCode::Unverified);
935 }
936
937 #[async_test]
941 async fn test_error_on_unsigned_of_verified_users() {
942 use VerificationViolationTestData as DataSet;
943
944 let machine = unsigned_of_verified_setup().await;
946
947 let carol_keys = DataSet::carol_keys_query_response_signed();
949 machine.mark_request_as_sent(&TransactionId::new(), &carol_keys).await.unwrap();
950
951 let carol_identity =
953 machine.get_identity(DataSet::carol_id(), None).await.unwrap().unwrap();
954 assert!(carol_identity.other().unwrap().is_verified());
955
956 let carol_unsigned_device = machine
957 .get_device(DataSet::carol_id(), DataSet::carol_unsigned_device_id(), None)
958 .await
959 .unwrap()
960 .unwrap();
961 assert!(!carol_unsigned_device.is_verified());
962
963 let encryption_settings = error_on_verification_problem_encryption_settings();
965 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
966 let share_result = collect_session_recipients(
967 machine.store(),
968 vec![DataSet::bob_id(), DataSet::carol_id()].into_iter(),
969 &encryption_settings,
970 &group_session,
971 )
972 .await;
973
974 assert_let!(
975 Err(OlmError::SessionRecipientCollectionError(
976 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
977 )) = share_result
978 );
979
980 assert_eq!(
982 unverified_devices,
983 BTreeMap::from([
984 (DataSet::bob_id().to_owned(), vec![DataSet::bob_device_2_id().to_owned()]),
985 (
986 DataSet::carol_id().to_owned(),
987 vec![DataSet::carol_unsigned_device_id().to_owned()]
988 ),
989 ])
990 );
991 }
992
993 #[async_test]
997 async fn test_error_on_unsigned_of_verified_resolve_by_whitelisting() {
998 use VerificationViolationTestData as DataSet;
999
1000 let machine = unsigned_of_verified_setup().await;
1001
1002 machine
1004 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1005 .await
1006 .unwrap()
1007 .unwrap()
1008 .set_local_trust(LocalTrust::Ignored)
1009 .await
1010 .unwrap();
1011
1012 let encryption_settings = error_on_verification_problem_encryption_settings();
1013 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1014
1015 let share_result = collect_session_recipients(
1017 machine.store(),
1018 iter::once(DataSet::bob_id()),
1019 &encryption_settings,
1020 &group_session,
1021 )
1022 .await
1023 .unwrap();
1024
1025 assert_eq!(2, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1026 assert_eq!(0, share_result.withheld_devices.len());
1027 }
1028
1029 #[async_test]
1033 async fn test_error_on_unsigned_of_verified_resolve_by_blacklisting() {
1034 use VerificationViolationTestData as DataSet;
1035
1036 let machine = unsigned_of_verified_setup().await;
1037
1038 machine
1040 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1041 .await
1042 .unwrap()
1043 .unwrap()
1044 .set_local_trust(LocalTrust::BlackListed)
1045 .await
1046 .unwrap();
1047
1048 let encryption_settings = error_on_verification_problem_encryption_settings();
1049 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1050
1051 let share_result = collect_session_recipients(
1053 machine.store(),
1054 iter::once(DataSet::bob_id()),
1055 &encryption_settings,
1056 &group_session,
1057 )
1058 .await
1059 .unwrap();
1060
1061 assert_eq!(1, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1062 let withheld_list: Vec<_> = share_result
1063 .withheld_devices
1064 .iter()
1065 .map(|(d, code)| (d.device_id().to_owned(), code.clone()))
1066 .collect();
1067 assert_eq!(
1068 withheld_list,
1069 vec![(DataSet::bob_device_2_id().to_owned(), WithheldCode::Blacklisted)]
1070 );
1071 }
1072
1073 #[async_test]
1077 async fn test_error_on_unsigned_of_verified_owner_is_us() {
1078 use VerificationViolationTestData as DataSet;
1079
1080 let machine = unsigned_of_verified_setup().await;
1081
1082 let mut own_keys = DataSet::own_keys_query_response_1().clone();
1084 own_keys.device_keys.insert(
1085 DataSet::own_id().to_owned(),
1086 BTreeMap::from([
1087 DataSet::own_signed_device_keys(),
1088 DataSet::own_unsigned_device_keys(),
1089 ]),
1090 );
1091 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1092
1093 let encryption_settings = error_on_verification_problem_encryption_settings();
1094 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1095 let share_result = collect_session_recipients(
1096 machine.store(),
1097 iter::once(DataSet::own_id()),
1098 &encryption_settings,
1099 &group_session,
1100 )
1101 .await;
1102
1103 assert_let!(
1104 Err(OlmError::SessionRecipientCollectionError(
1105 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
1106 )) = share_result
1107 );
1108
1109 assert_eq!(
1111 unverified_devices,
1112 BTreeMap::from([(
1113 DataSet::own_id().to_owned(),
1114 vec![DataSet::own_unsigned_device_id()]
1115 ),])
1116 );
1117 }
1118
1119 #[async_test]
1122 async fn test_should_not_error_on_unsigned_of_unverified() {
1123 use VerificationViolationTestData as DataSet;
1124
1125 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1126
1127 let own_keys = DataSet::own_keys_query_response_1();
1129 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1130
1131 machine
1133 .import_cross_signing_keys(CrossSigningKeyExport {
1134 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1135 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1136 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1137 })
1138 .await
1139 .unwrap();
1140
1141 let bob_keys = DataSet::bob_keys_query_response_rotated();
1143 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1144
1145 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1148 assert!(!bob_identity.other().unwrap().is_verified());
1149
1150 let bob_unsigned_device = machine
1151 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
1152 .await
1153 .unwrap()
1154 .unwrap();
1155 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1156
1157 let encryption_settings = error_on_verification_problem_encryption_settings();
1158 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1159 collect_session_recipients(
1160 machine.store(),
1161 iter::once(DataSet::bob_id()),
1162 &encryption_settings,
1163 &group_session,
1164 )
1165 .await
1166 .unwrap();
1167 }
1168
1169 #[async_test]
1172 async fn test_should_not_error_on_unsigned_of_signed_but_unverified() {
1173 use VerificationViolationTestData as DataSet;
1174
1175 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1176
1177 let keys_query = DataSet::own_keys_query_response_1();
1179 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1180
1181 let keys_query = DataSet::bob_keys_query_response_signed();
1183 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1184
1185 let bob_identity =
1188 machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap().other().unwrap();
1189 assert!(bob_identity
1190 .own_identity
1191 .as_ref()
1192 .unwrap()
1193 .is_identity_signed(&bob_identity.inner));
1194 assert!(!bob_identity.is_verified());
1195
1196 let bob_unsigned_device = machine
1197 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1198 .await
1199 .unwrap()
1200 .unwrap();
1201 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1202
1203 let encryption_settings = error_on_verification_problem_encryption_settings();
1205 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1206 collect_session_recipients(
1207 machine.store(),
1208 iter::once(DataSet::bob_id()),
1209 &encryption_settings,
1210 &group_session,
1211 )
1212 .await
1213 .unwrap();
1214 }
1215
1216 #[async_test]
1220 async fn test_verified_user_changed_identity() {
1221 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1222
1223 let machine = unsigned_of_verified_setup().await;
1226
1227 let bob_keys = DataSet::bob_keys_query_response_rotated();
1229 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1230
1231 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1233 assert!(bob_identity.has_verification_violation());
1234
1235 let encryption_settings = error_on_verification_problem_encryption_settings();
1237 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1238 let share_result = collect_session_recipients(
1239 machine.store(),
1240 iter::once(DataSet::bob_id()),
1241 &encryption_settings,
1242 &group_session,
1243 )
1244 .await;
1245
1246 assert_let!(
1247 Err(OlmError::SessionRecipientCollectionError(
1248 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1249 )) = share_result
1250 );
1251 assert_eq!(violating_users, vec![DataSet::bob_id()]);
1252
1253 bob_identity.withdraw_verification().await.unwrap();
1255
1256 collect_session_recipients(
1257 machine.store(),
1258 iter::once(DataSet::bob_id()),
1259 &encryption_settings,
1260 &group_session,
1261 )
1262 .await
1263 .unwrap();
1264 }
1265
1266 #[async_test]
1270 async fn test_own_verified_identity_changed() {
1271 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1272
1273 let machine = unsigned_of_verified_setup().await;
1275 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1276 assert!(own_identity.own().unwrap().is_verified());
1277
1278 let own_keys = DataSet::own_keys_query_response_2();
1280 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1281
1282 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1283 assert!(!own_identity.is_verified());
1284
1285 let encryption_settings = error_on_verification_problem_encryption_settings();
1287 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1288 let share_result = collect_session_recipients(
1289 machine.store(),
1290 iter::once(DataSet::own_id()),
1291 &encryption_settings,
1292 &group_session,
1293 )
1294 .await;
1295
1296 assert_let!(
1297 Err(OlmError::SessionRecipientCollectionError(
1298 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1299 )) = share_result
1300 );
1301 assert_eq!(violating_users, vec![DataSet::own_id()]);
1302
1303 own_identity.withdraw_verification().await.unwrap();
1305
1306 collect_session_recipients(
1307 machine.store(),
1308 iter::once(DataSet::own_id()),
1309 &encryption_settings,
1310 &group_session,
1311 )
1312 .await
1313 .unwrap();
1314 }
1315
1316 mod dehydrated_device {
1319 use std::{collections::HashSet, iter};
1320
1321 use insta::{allow_duplicates, assert_json_snapshot, with_settings};
1322 use matrix_sdk_common::deserialized_responses::WithheldCode;
1323 use matrix_sdk_test::{
1324 async_test, ruma_response_to_json,
1325 test_json::keys_query_sets::{
1326 KeyDistributionTestData, KeyQueryResponseTemplate,
1327 KeyQueryResponseTemplateDeviceOptions,
1328 },
1329 };
1330 use ruma::{device_id, user_id, DeviceId, TransactionId, UserId};
1331 use vodozemac::{Curve25519PublicKey, Ed25519SecretKey};
1332
1333 use super::{
1334 all_devices_strategy_settings, create_test_outbound_group_session,
1335 error_on_verification_problem_encryption_settings, identity_based_strategy_settings,
1336 test_machine,
1337 };
1338 use crate::{
1339 session_manager::group_sessions::{
1340 share_strategy::collect_session_recipients, CollectRecipientsResult,
1341 },
1342 EncryptionSettings, OlmMachine,
1343 };
1344
1345 #[async_test]
1346 async fn test_all_devices_strategy_should_share_with_verified_dehydrated_device() {
1347 should_share_with_verified_dehydrated_device(&all_devices_strategy_settings()).await
1348 }
1349
1350 #[async_test]
1351 async fn test_error_on_verification_problem_strategy_should_share_with_verified_dehydrated_device(
1352 ) {
1353 should_share_with_verified_dehydrated_device(
1354 &error_on_verification_problem_encryption_settings(),
1355 )
1356 .await
1357 }
1358
1359 #[async_test]
1360 async fn test_identity_based_strategy_should_share_with_verified_dehydrated_device() {
1361 should_share_with_verified_dehydrated_device(&identity_based_strategy_settings()).await
1362 }
1363
1364 async fn should_share_with_verified_dehydrated_device(
1369 encryption_settings: &EncryptionSettings,
1370 ) {
1371 let machine = test_machine().await;
1372
1373 let bob_user_id = user_id!("@bob:localhost");
1376 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1377 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1378 .with_dehydrated_device(bob_dehydrated_device_id, true)
1379 .build_response();
1380 allow_duplicates! {
1381 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1382 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1383 });
1384 }
1385 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1386
1387 let recips = share_test_session_and_collect_recipients(
1389 &machine,
1390 bob_user_id,
1391 encryption_settings,
1392 )
1393 .await;
1394
1395 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1397 }
1398
1399 #[async_test]
1400 async fn test_all_devices_strategy_should_not_share_with_unverified_dehydrated_device() {
1401 should_not_share_with_unverified_dehydrated_device(&all_devices_strategy_settings())
1402 .await
1403 }
1404
1405 #[async_test]
1406 async fn test_error_on_verification_problem_strategy_should_not_share_with_unverified_dehydrated_device(
1407 ) {
1408 should_not_share_with_unverified_dehydrated_device(
1409 &error_on_verification_problem_encryption_settings(),
1410 )
1411 .await
1412 }
1413
1414 #[async_test]
1415 async fn test_identity_based_strategy_should_not_share_with_unverified_dehydrated_device() {
1416 should_not_share_with_unverified_dehydrated_device(&identity_based_strategy_settings())
1417 .await
1418 }
1419
1420 async fn should_not_share_with_unverified_dehydrated_device(
1425 encryption_settings: &EncryptionSettings,
1426 ) {
1427 let machine = test_machine().await;
1428
1429 let bob_user_id = user_id!("@bob:localhost");
1432 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1433 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1434 .with_dehydrated_device(bob_dehydrated_device_id, false)
1435 .build_response();
1436 allow_duplicates! {
1437 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1438 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1439 });
1440 }
1441 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1442
1443 let recips = share_test_session_and_collect_recipients(
1445 &machine,
1446 bob_user_id,
1447 encryption_settings,
1448 )
1449 .await;
1450
1451 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1454 }
1455
1456 #[async_test]
1457 async fn test_all_devices_strategy_should_share_with_verified_device_of_pin_violation_user()
1458 {
1459 should_share_with_verified_device_of_pin_violation_user(
1460 &all_devices_strategy_settings(),
1461 )
1462 .await
1463 }
1464
1465 #[async_test]
1466 async fn test_error_on_verification_problem_strategy_should_share_with_verified_device_of_pin_violation_user(
1467 ) {
1468 should_share_with_verified_device_of_pin_violation_user(
1469 &error_on_verification_problem_encryption_settings(),
1470 )
1471 .await
1472 }
1473
1474 #[async_test]
1475 async fn test_identity_based_strategy_should_share_with_verified_device_of_pin_violation_user(
1476 ) {
1477 should_share_with_verified_device_of_pin_violation_user(
1478 &identity_based_strategy_settings(),
1479 )
1480 .await
1481 }
1482
1483 async fn should_share_with_verified_device_of_pin_violation_user(
1488 encryption_settings: &EncryptionSettings,
1489 ) {
1490 let machine = test_machine().await;
1491
1492 let bob_user_id = user_id!("@bob:localhost");
1494 let keys_query =
1495 key_query_response_template_with_cross_signing(bob_user_id).build_response();
1496 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1497
1498 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1501 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1502 .with_dehydrated_device(bob_dehydrated_device_id, true)
1503 .build_response();
1504 allow_duplicates! {
1505 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1506 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1507 });
1508 }
1509 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1510
1511 let recips = share_test_session_and_collect_recipients(
1513 &machine,
1514 bob_user_id,
1515 encryption_settings,
1516 )
1517 .await;
1518
1519 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1521 }
1522
1523 #[async_test]
1524 async fn test_all_devices_strategy_should_not_share_with_dehydrated_device_of_verification_violation_user(
1525 ) {
1526 should_not_share_with_dehydrated_device_of_verification_violation_user(
1527 &all_devices_strategy_settings(),
1528 )
1529 .await
1530 }
1531
1532 async fn should_not_share_with_dehydrated_device_of_verification_violation_user(
1535 encryption_settings: &EncryptionSettings,
1536 ) {
1537 let bob_user_id = user_id!("@bob:localhost");
1538 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1539 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1540 bob_user_id,
1541 bob_dehydrated_device_id,
1542 )
1543 .await;
1544
1545 let recips = share_test_session_and_collect_recipients(
1547 &machine,
1548 bob_user_id,
1549 encryption_settings,
1550 )
1551 .await;
1552
1553 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1556 }
1557
1558 #[async_test]
1559 async fn test_error_on_verification_problem_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1560 ) {
1561 should_give_error_for_dehydrated_device_of_verification_violation_user(
1562 &error_on_verification_problem_encryption_settings(),
1563 )
1564 .await
1565 }
1566
1567 #[async_test]
1568 async fn test_identity_based_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1569 ) {
1570 should_give_error_for_dehydrated_device_of_verification_violation_user(
1574 &identity_based_strategy_settings(),
1575 )
1576 .await
1577 }
1578
1579 async fn should_give_error_for_dehydrated_device_of_verification_violation_user(
1583 encryption_settings: &EncryptionSettings,
1584 ) {
1585 let bob_user_id = user_id!("@bob:localhost");
1586 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1587 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1588 bob_user_id,
1589 bob_dehydrated_device_id,
1590 )
1591 .await;
1592
1593 let group_session = create_test_outbound_group_session(&machine, encryption_settings);
1594 let share_result = collect_session_recipients(
1595 machine.store(),
1596 iter::once(bob_user_id),
1597 encryption_settings,
1598 &group_session,
1599 )
1600 .await;
1601
1602 assert_matches::assert_matches!(
1605 share_result,
1606 Err(crate::OlmError::SessionRecipientCollectionError(
1607 crate::SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
1608 ))
1609 );
1610 }
1611
1612 async fn prepare_machine_with_dehydrated_device_of_verification_violation_user(
1616 bob_user_id: &UserId,
1617 bob_dehydrated_device_id: &DeviceId,
1618 ) -> OlmMachine {
1619 let machine = test_machine().await;
1620
1621 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1623 .with_user_verification_signature(
1624 KeyDistributionTestData::me_id(),
1625 &KeyDistributionTestData::me_private_user_signing_key(),
1626 )
1627 .build_response();
1628 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1629
1630 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1633 .with_dehydrated_device(bob_dehydrated_device_id, true)
1634 .build_response();
1635 allow_duplicates! {
1636 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1637 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1638 });
1639 }
1640 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1641
1642 machine
1643 }
1644
1645 async fn share_test_session_and_collect_recipients(
1648 machine: &OlmMachine,
1649 target_user_id: &UserId,
1650 encryption_settings: &EncryptionSettings,
1651 ) -> CollectRecipientsResult {
1652 let group_session = create_test_outbound_group_session(machine, encryption_settings);
1653 collect_session_recipients(
1654 machine.store(),
1655 iter::once(target_user_id),
1656 encryption_settings,
1657 &group_session,
1658 )
1659 .await
1660 .unwrap()
1661 }
1662
1663 fn assert_shared_with(
1666 recips: CollectRecipientsResult,
1667 user_id: &UserId,
1668 device_ids: HashSet<&DeviceId>,
1669 ) {
1670 let bob_devices_shared: HashSet<_> = recips
1671 .devices
1672 .get(user_id)
1673 .unwrap_or_else(|| panic!("session not shared with {user_id}"))
1674 .iter()
1675 .map(|d| d.device_id())
1676 .collect();
1677 assert_eq!(bob_devices_shared, device_ids);
1678
1679 assert!(recips.withheld_devices.is_empty(), "Unexpected withheld messages");
1680 }
1681
1682 fn assert_withheld_to(
1685 recips: CollectRecipientsResult,
1686 bob_user_id: &UserId,
1687 bob_dehydrated_device_id: &DeviceId,
1688 ) {
1689 for (user, device_list) in recips.devices {
1691 assert_eq!(device_list.len(), 0, "session unexpectedly shared with {}", user);
1692 }
1693
1694 assert_eq!(recips.withheld_devices.len(), 1);
1696 assert_eq!(recips.withheld_devices[0].0.user_id(), bob_user_id);
1697 assert_eq!(recips.withheld_devices[0].0.device_id(), bob_dehydrated_device_id);
1698 assert_eq!(recips.withheld_devices[0].1, WithheldCode::Unverified);
1699 }
1700
1701 fn key_query_response_template_with_cross_signing(
1704 user_id: &UserId,
1705 ) -> KeyQueryResponseTemplate {
1706 KeyQueryResponseTemplate::new(user_id.to_owned()).with_cross_signing_keys(
1707 Ed25519SecretKey::from_slice(b"master12master12master12master12"),
1708 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1709 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1710 )
1711 }
1712
1713 fn key_query_response_template_with_changed_cross_signing(
1717 bob_user_id: &UserId,
1718 ) -> KeyQueryResponseTemplate {
1719 KeyQueryResponseTemplate::new(bob_user_id.to_owned()).with_cross_signing_keys(
1720 Ed25519SecretKey::from_slice(b"newmaster__newmaster__newmaster_"),
1721 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1722 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1723 )
1724 }
1725
1726 trait KeyQueryResponseTemplateExt {
1727 fn with_dehydrated_device(
1728 self,
1729 device_id: &DeviceId,
1730 verified: bool,
1731 ) -> KeyQueryResponseTemplate;
1732 }
1733
1734 impl KeyQueryResponseTemplateExt for KeyQueryResponseTemplate {
1735 fn with_dehydrated_device(
1737 self,
1738 device_id: &DeviceId,
1739 verified: bool,
1740 ) -> KeyQueryResponseTemplate {
1741 self.with_device(
1742 device_id,
1743 &Curve25519PublicKey::from(b"curvepubcurvepubcurvepubcurvepub".to_owned()),
1744 &Ed25519SecretKey::from_slice(b"device12device12device12device12"),
1745 KeyQueryResponseTemplateDeviceOptions::new()
1746 .dehydrated(true)
1747 .verified(verified),
1748 )
1749 }
1750 }
1751 }
1752
1753 #[async_test]
1754 async fn test_share_with_identity_strategy() {
1755 let machine = test_machine().await;
1756 import_known_users_to_test_machine(&machine).await;
1757
1758 let encryption_settings = identity_based_strategy_settings();
1759
1760 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1761
1762 let share_result = collect_session_recipients(
1763 machine.store(),
1764 vec![
1765 KeyDistributionTestData::dan_id(),
1766 KeyDistributionTestData::dave_id(),
1767 KeyDistributionTestData::good_id(),
1768 ]
1769 .into_iter(),
1770 &encryption_settings,
1771 &group_session,
1772 )
1773 .await
1774 .unwrap();
1775
1776 assert!(!share_result.should_rotate);
1777
1778 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
1779 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
1780 assert!(dave_devices_shared.unwrap().is_empty());
1782
1783 assert_eq!(good_devices_shared.unwrap().len(), 2);
1785
1786 let dan_devices_shared =
1789 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
1790
1791 assert_eq!(dan_devices_shared.len(), 1);
1792 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
1793 assert_eq!(
1794 dan_device_that_will_get_the_key.device_id().as_str(),
1795 KeyDistributionTestData::dan_signed_device_id()
1796 );
1797
1798 let (_, code) = share_result
1800 .withheld_devices
1801 .iter()
1802 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
1803 .expect("This dan's device should receive a withheld code");
1804
1805 assert_eq!(code, &WithheldCode::Unverified);
1806
1807 let (_, code) = share_result
1809 .withheld_devices
1810 .iter()
1811 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
1812 .expect("This dave device should receive a withheld code");
1813
1814 assert_eq!(code, &WithheldCode::Unverified);
1815 }
1816
1817 #[async_test]
1820 async fn test_share_identity_strategy_no_cross_signing() {
1821 let machine: OlmMachine = OlmMachine::new(
1824 KeyDistributionTestData::me_id(),
1825 KeyDistributionTestData::me_device_id(),
1826 )
1827 .await;
1828
1829 let keys_query = KeyDistributionTestData::dan_keys_query_response();
1830 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1831
1832 let fake_room_id = room_id!("!roomid:localhost");
1833
1834 let encryption_settings = identity_based_strategy_settings();
1835
1836 let request_result = machine
1837 .share_room_key(
1838 fake_room_id,
1839 iter::once(KeyDistributionTestData::dan_id()),
1840 encryption_settings.clone(),
1841 )
1842 .await;
1843
1844 assert_matches!(
1845 request_result,
1846 Err(OlmError::SessionRecipientCollectionError(
1847 SessionRecipientCollectionError::CrossSigningNotSetup
1848 ))
1849 );
1850
1851 let keys_query = KeyDistributionTestData::me_keys_query_response();
1855 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1856
1857 let request_result = machine
1858 .share_room_key(
1859 fake_room_id,
1860 iter::once(KeyDistributionTestData::dan_id()),
1861 encryption_settings.clone(),
1862 )
1863 .await;
1864
1865 assert_matches!(
1866 request_result,
1867 Err(OlmError::SessionRecipientCollectionError(
1868 SessionRecipientCollectionError::SendingFromUnverifiedDevice
1869 ))
1870 );
1871
1872 machine
1875 .import_cross_signing_keys(CrossSigningKeyExport {
1876 master_key: KeyDistributionTestData::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1877 self_signing_key: KeyDistributionTestData::SELF_SIGNING_KEY_PRIVATE_EXPORT
1878 .to_owned()
1879 .into(),
1880 user_signing_key: KeyDistributionTestData::USER_SIGNING_KEY_PRIVATE_EXPORT
1881 .to_owned()
1882 .into(),
1883 })
1884 .await
1885 .unwrap();
1886
1887 let requests = machine
1888 .share_room_key(
1889 fake_room_id,
1890 iter::once(KeyDistributionTestData::dan_id()),
1891 encryption_settings.clone(),
1892 )
1893 .await
1894 .unwrap();
1895
1896 assert_eq!(requests.len(), 1);
1899 }
1900
1901 #[async_test]
1905 async fn test_share_identity_strategy_report_verification_violation() {
1906 let machine: OlmMachine = OlmMachine::new(
1907 KeyDistributionTestData::me_id(),
1908 KeyDistributionTestData::me_device_id(),
1909 )
1910 .await;
1911
1912 machine.bootstrap_cross_signing(false).await.unwrap();
1913
1914 let user1 = IdentityChangeDataSet::user_id();
1916 let user2 = MaloIdentityChangeDataSet::user_id();
1917
1918 let keys_query = IdentityChangeDataSet::key_query_with_identity_a();
1920 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1921
1922 let keys_query = MaloIdentityChangeDataSet::initial_key_query();
1923 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1924
1925 let keys_query = IdentityChangeDataSet::key_query_with_identity_b();
1929 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1930 machine
1931 .get_identity(user1, None)
1932 .await
1933 .unwrap()
1934 .unwrap()
1935 .other()
1936 .unwrap()
1937 .mark_as_previously_verified()
1938 .await
1939 .unwrap();
1940
1941 let keys_query = MaloIdentityChangeDataSet::updated_key_query();
1942 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1943 machine
1944 .get_identity(user2, None)
1945 .await
1946 .unwrap()
1947 .unwrap()
1948 .other()
1949 .unwrap()
1950 .mark_as_previously_verified()
1951 .await
1952 .unwrap();
1953
1954 let fake_room_id = room_id!("!roomid:localhost");
1955
1956 let encryption_settings = identity_based_strategy_settings();
1958
1959 let request_result = machine
1960 .share_room_key(
1961 fake_room_id,
1962 vec![user1, user2].into_iter(),
1963 encryption_settings.clone(),
1964 )
1965 .await;
1966
1967 assert_let!(
1970 Err(OlmError::SessionRecipientCollectionError(
1971 SessionRecipientCollectionError::VerifiedUserChangedIdentity(affected_users)
1972 )) = request_result
1973 );
1974 assert_eq!(2, affected_users.len());
1976
1977 machine
1979 .get_identity(user1, None)
1980 .await
1981 .unwrap()
1982 .unwrap()
1983 .withdraw_verification()
1984 .await
1985 .unwrap();
1986
1987 let verification_request = machine
1989 .get_identity(user2, None)
1990 .await
1991 .unwrap()
1992 .unwrap()
1993 .other()
1994 .unwrap()
1995 .verify()
1996 .await
1997 .unwrap();
1998
1999 let master_key =
2000 &machine.get_identity(user2, None).await.unwrap().unwrap().other().unwrap().master_key;
2001
2002 let my_identity = machine
2003 .get_identity(KeyDistributionTestData::me_id(), None)
2004 .await
2005 .expect("Should not fail to find own identity")
2006 .expect("Our own identity should not be missing")
2007 .own()
2008 .expect("Our own identity should be of type Own");
2009
2010 let msk = json!({ user2: serde_json::to_value(master_key).expect("Should not fail to serialize")});
2011 let ssk =
2012 serde_json::to_value(&MaloIdentityChangeDataSet::updated_key_query().self_signing_keys)
2013 .expect("Should not fail to serialize");
2014
2015 let kq_response = simulate_key_query_response_for_verification(
2016 verification_request,
2017 my_identity,
2018 KeyDistributionTestData::me_id(),
2019 user2,
2020 msk,
2021 ssk,
2022 );
2023
2024 machine
2025 .mark_request_as_sent(
2026 &TransactionId::new(),
2027 crate::types::requests::AnyIncomingResponse::KeysQuery(&kq_response),
2028 )
2029 .await
2030 .unwrap();
2031
2032 assert!(machine.get_identity(user2, None).await.unwrap().unwrap().is_verified());
2033
2034 machine
2036 .share_room_key(
2037 fake_room_id,
2038 vec![user1, user2].into_iter(),
2039 encryption_settings.clone(),
2040 )
2041 .await
2042 .unwrap();
2043 }
2044
2045 #[async_test]
2046 async fn test_should_rotate_based_on_visibility() {
2047 let machine = test_machine().await;
2048 import_known_users_to_test_machine(&machine).await;
2049
2050 let strategy = CollectStrategy::AllDevices;
2051
2052 let encryption_settings = EncryptionSettings {
2053 sharing_strategy: strategy.clone(),
2054 history_visibility: HistoryVisibility::Invited,
2055 ..Default::default()
2056 };
2057
2058 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2059
2060 let _ = collect_session_recipients(
2061 machine.store(),
2062 vec![KeyDistributionTestData::dan_id()].into_iter(),
2063 &encryption_settings,
2064 &group_session,
2065 )
2066 .await
2067 .unwrap();
2068
2069 let encryption_settings = EncryptionSettings {
2071 sharing_strategy: strategy.clone(),
2072 history_visibility: HistoryVisibility::Shared,
2073 ..Default::default()
2074 };
2075
2076 let share_result = collect_session_recipients(
2077 machine.store(),
2078 vec![KeyDistributionTestData::dan_id()].into_iter(),
2079 &encryption_settings,
2080 &group_session,
2081 )
2082 .await
2083 .unwrap();
2084
2085 assert!(share_result.should_rotate);
2086 }
2087
2088 #[async_test]
2092 async fn test_should_rotate_based_on_device_excluded() {
2093 let machine = test_machine().await;
2094 import_known_users_to_test_machine(&machine).await;
2095
2096 let encryption_settings = all_devices_strategy_settings();
2097 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2098 let sender_key = machine.identity_keys().curve25519;
2099
2100 group_session
2101 .mark_shared_with(
2102 KeyDistributionTestData::dan_id(),
2103 KeyDistributionTestData::dan_signed_device_id(),
2104 sender_key,
2105 )
2106 .await;
2107 group_session
2108 .mark_shared_with(
2109 KeyDistributionTestData::dan_id(),
2110 KeyDistributionTestData::dan_unsigned_device_id(),
2111 sender_key,
2112 )
2113 .await;
2114
2115 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2117 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2118
2119 let share_result = collect_session_recipients(
2121 machine.store(),
2122 vec![KeyDistributionTestData::dan_id()].into_iter(),
2123 &encryption_settings,
2124 &group_session,
2125 )
2126 .await
2127 .unwrap();
2128
2129 assert!(share_result.should_rotate);
2130 }
2131
2132 #[async_test]
2135 async fn test_should_rotate_based_on_device_with_pending_request_excluded() {
2136 let machine = test_machine().await;
2137 import_known_users_to_test_machine(&machine).await;
2138
2139 let encryption_settings = all_devices_strategy_settings();
2140 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2141 let sender_key = machine.identity_keys().curve25519;
2142
2143 let dan_user = KeyDistributionTestData::dan_id();
2144 let dan_dev1 = KeyDistributionTestData::dan_signed_device_id();
2145 let dan_dev2 = KeyDistributionTestData::dan_unsigned_device_id();
2146
2147 group_session.mark_shared_with(dan_user, dan_dev1, sender_key).await;
2149
2150 {
2151 let share_infos = BTreeMap::from([(
2153 dan_user.to_owned(),
2154 BTreeMap::from([(
2155 dan_dev2.to_owned(),
2156 ShareInfo::new_shared(sender_key, 0, SequenceNumber::default()),
2157 )]),
2158 )]);
2159
2160 let txid = TransactionId::new();
2161 let req = Arc::new(ToDeviceRequest::for_recipients(
2162 dan_user,
2163 vec![dan_dev2.to_owned()],
2164 &ruma::events::AnyToDeviceEventContent::Dummy(ToDeviceDummyEventContent),
2165 txid.clone(),
2166 ));
2167 group_session.add_request(txid, req, share_infos);
2168 }
2169
2170 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2172 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2173
2174 let share_result = collect_session_recipients(
2176 machine.store(),
2177 vec![KeyDistributionTestData::dan_id()].into_iter(),
2178 &encryption_settings,
2179 &group_session,
2180 )
2181 .await
2182 .unwrap();
2183
2184 assert!(share_result.should_rotate);
2185 }
2186
2187 #[async_test]
2190 async fn test_should_not_rotate_if_keys_were_withheld() {
2191 let machine = test_machine().await;
2192 import_known_users_to_test_machine(&machine).await;
2193
2194 let encryption_settings = all_devices_strategy_settings();
2195 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2196 let fake_room_id = group_session.room_id();
2197
2198 let requests = machine
2201 .share_room_key(
2202 fake_room_id,
2203 vec![KeyDistributionTestData::dan_id()].into_iter(),
2204 encryption_settings.clone(),
2205 )
2206 .await
2207 .unwrap();
2208
2209 for r in requests {
2210 machine
2211 .inner
2212 .group_session_manager
2213 .mark_request_as_sent(r.as_ref().txn_id.as_ref())
2214 .await
2215 .unwrap();
2216 }
2217
2218 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2220 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2221
2222 let share_result = collect_session_recipients(
2224 machine.store(),
2225 vec![KeyDistributionTestData::dan_id()].into_iter(),
2226 &encryption_settings,
2227 &group_session,
2228 )
2229 .await
2230 .unwrap();
2231
2232 assert!(!share_result.should_rotate);
2233 }
2234
2235 async fn unsigned_of_verified_setup() -> OlmMachine {
2243 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
2244
2245 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
2246
2247 let own_keys = DataSet::own_keys_query_response_1();
2249 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
2250
2251 machine
2253 .import_cross_signing_keys(CrossSigningKeyExport {
2254 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
2255 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2256 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2257 })
2258 .await
2259 .unwrap();
2260
2261 let bob_keys = DataSet::bob_keys_query_response_signed();
2263 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
2264
2265 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
2268 assert!(bob_identity.other().unwrap().is_verified());
2269
2270 let bob_signed_device = machine
2271 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
2272 .await
2273 .unwrap()
2274 .unwrap();
2275 assert!(bob_signed_device.is_verified());
2276 assert!(bob_signed_device.device_owner_identity.is_some());
2277
2278 let bob_unsigned_device = machine
2279 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
2280 .await
2281 .unwrap()
2282 .unwrap();
2283 assert!(!bob_unsigned_device.is_verified());
2284
2285 machine
2286 }
2287
2288 fn all_devices_strategy_settings() -> EncryptionSettings {
2290 EncryptionSettings { sharing_strategy: CollectStrategy::AllDevices, ..Default::default() }
2291 }
2292
2293 fn error_on_verification_problem_encryption_settings() -> EncryptionSettings {
2296 EncryptionSettings {
2297 sharing_strategy: CollectStrategy::ErrorOnVerifiedUserProblem,
2298 ..Default::default()
2299 }
2300 }
2301
2302 fn identity_based_strategy_settings() -> EncryptionSettings {
2304 EncryptionSettings {
2305 sharing_strategy: CollectStrategy::IdentityBasedStrategy,
2306 ..Default::default()
2307 }
2308 }
2309
2310 fn create_test_outbound_group_session(
2313 machine: &OlmMachine,
2314 encryption_settings: &EncryptionSettings,
2315 ) -> OutboundGroupSession {
2316 OutboundGroupSession::new(
2317 machine.device_id().into(),
2318 Arc::new(machine.identity_keys()),
2319 room_id!("!roomid:localhost"),
2320 encryption_settings.clone(),
2321 )
2322 .expect("creating an outbound group session should not fail")
2323 }
2324}