1use std::{
16 collections::{BTreeMap, BTreeSet, HashMap},
17 default::Default,
18 ops::Deref,
19};
20
21use itertools::{Either, Itertools};
22use matrix_sdk_common::deserialized_responses::WithheldCode;
23use ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId};
24use serde::{Deserialize, Serialize};
25use tracing::{debug, instrument, trace};
26
27use super::OutboundGroupSession;
28use crate::{
29 error::{OlmResult, SessionRecipientCollectionError},
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 users: BTreeSet<&UserId> = users.collect();
160 let mut result = CollectRecipientsResult::default();
161 let mut verified_users_with_new_identities: Vec<OwnedUserId> = Default::default();
162
163 trace!(?users, ?settings, "Calculating group session recipients");
164
165 let users_shared_with: BTreeSet<OwnedUserId> =
166 outbound.shared_with_set.read().keys().cloned().collect();
167
168 let users_shared_with: BTreeSet<&UserId> = users_shared_with.iter().map(Deref::deref).collect();
169
170 let user_left = !users_shared_with.difference(&users).collect::<BTreeSet<_>>().is_empty();
173
174 let visibility_changed = outbound.settings().history_visibility != settings.history_visibility;
175 let algorithm_changed = outbound.settings().algorithm != settings.algorithm;
176
177 result.should_rotate = user_left || visibility_changed || algorithm_changed;
186
187 let own_identity = store.get_user_identity(store.user_id()).await?.and_then(|i| i.into_own());
188
189 match settings.sharing_strategy {
191 CollectStrategy::AllDevices => {
192 for user_id in users {
193 trace!(
194 "CollectStrategy::AllDevices: Considering recipient devices for user {}",
195 user_id
196 );
197 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
198 let device_owner_identity = store.get_user_identity(user_id).await?;
199
200 let recipient_devices = split_devices_for_user_for_all_devices_strategy(
201 user_devices,
202 &own_identity,
203 &device_owner_identity,
204 );
205 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
206 }
207 }
208 CollectStrategy::ErrorOnVerifiedUserProblem => {
209 let mut unsigned_devices_of_verified_users: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>> =
210 Default::default();
211
212 for user_id in users {
213 trace!("CollectStrategy::ErrorOnVerifiedUserProblem: Considering recipient devices for user {}", user_id);
214 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
215
216 let device_owner_identity = store.get_user_identity(user_id).await?;
217
218 if has_identity_verification_violation(
219 own_identity.as_ref(),
220 device_owner_identity.as_ref(),
221 ) {
222 verified_users_with_new_identities.push(user_id.to_owned());
223 continue;
225 }
226
227 let recipient_devices =
228 split_devices_for_user_for_error_on_verified_user_problem_strategy(
229 user_devices,
230 &own_identity,
231 &device_owner_identity,
232 );
233
234 match recipient_devices {
235 ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices) => {
236 unsigned_devices_of_verified_users.insert(user_id.to_owned(), devices);
237 }
238 ErrorOnVerifiedUserProblemResult::Devices(recipient_devices) => {
239 update_recipients_for_user(
240 &mut result,
241 outbound,
242 user_id,
243 recipient_devices,
244 );
245 }
246 }
247 }
248
249 if !unsigned_devices_of_verified_users.is_empty() {
253 return Err(OlmError::SessionRecipientCollectionError(
254 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
255 unsigned_devices_of_verified_users,
256 ),
257 ));
258 }
259 }
260 CollectStrategy::IdentityBasedStrategy => {
261 match &own_identity {
264 None => {
265 return Err(OlmError::SessionRecipientCollectionError(
266 SessionRecipientCollectionError::CrossSigningNotSetup,
267 ))
268 }
269 Some(identity) if !identity.is_verified() => {
270 return Err(OlmError::SessionRecipientCollectionError(
271 SessionRecipientCollectionError::SendingFromUnverifiedDevice,
272 ))
273 }
274 Some(_) => (),
275 }
276
277 for user_id in users {
278 trace!("CollectStrategy::IdentityBasedStrategy: Considering recipient devices for user {}", user_id);
279 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
280
281 let device_owner_identity = store.get_user_identity(user_id).await?;
282
283 if has_identity_verification_violation(
284 own_identity.as_ref(),
285 device_owner_identity.as_ref(),
286 ) {
287 verified_users_with_new_identities.push(user_id.to_owned());
288 continue;
290 }
291
292 let recipient_devices = split_devices_for_user_for_identity_based_strategy(
293 user_devices,
294 &device_owner_identity,
295 );
296
297 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
298 }
299 }
300
301 CollectStrategy::OnlyTrustedDevices => {
302 for user_id in users {
303 trace!("CollectStrategy::OnlyTrustedDevices: Considering recipient devices for user {}", user_id);
304 let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
305 let device_owner_identity = store.get_user_identity(user_id).await?;
306
307 let recipient_devices = split_devices_for_user_for_only_trusted_devices(
308 user_devices,
309 &own_identity,
310 &device_owner_identity,
311 );
312
313 update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
314 }
315 }
316 }
317
318 if !verified_users_with_new_identities.is_empty() {
321 return Err(OlmError::SessionRecipientCollectionError(
322 SessionRecipientCollectionError::VerifiedUserChangedIdentity(
323 verified_users_with_new_identities,
324 ),
325 ));
326 }
327
328 if result.should_rotate {
329 debug!(
330 result.should_rotate,
331 user_left,
332 visibility_changed,
333 algorithm_changed,
334 "Rotating room key to protect room history",
335 );
336 }
337 trace!(result.should_rotate, "Done calculating group session recipients");
338
339 Ok(result)
340}
341
342fn update_recipients_for_user(
345 recipients: &mut CollectRecipientsResult,
346 outbound: &OutboundGroupSession,
347 user_id: &UserId,
348 recipient_devices: RecipientDevicesForUser,
349) {
350 if !recipients.should_rotate {
355 recipients.should_rotate =
356 is_session_overshared_for_user(outbound, user_id, &recipient_devices.allowed_devices)
357 }
358
359 recipients
360 .devices
361 .entry(user_id.to_owned())
362 .or_default()
363 .extend(recipient_devices.allowed_devices);
364 recipients.withheld_devices.extend(recipient_devices.denied_devices_with_code);
365}
366
367fn is_session_overshared_for_user(
383 outbound_session: &OutboundGroupSession,
384 user_id: &UserId,
385 recipient_devices: &[DeviceData],
386) -> bool {
387 let recipient_device_ids: BTreeSet<&DeviceId> =
389 recipient_devices.iter().map(|d| d.device_id()).collect();
390
391 let guard = outbound_session.shared_with_set.read();
392
393 let Some(shared) = guard.get(user_id) else {
394 return false;
395 };
396
397 let shared: BTreeSet<&DeviceId> = shared.keys().map(|d| d.as_ref()).collect();
399
400 let newly_deleted_or_blacklisted =
408 shared.difference(&recipient_device_ids).collect::<BTreeSet<_>>();
409
410 let should_rotate = !newly_deleted_or_blacklisted.is_empty();
411 if should_rotate {
412 debug!(
413 "Rotating a room key due to these devices being deleted/blacklisted {:?}",
414 newly_deleted_or_blacklisted,
415 );
416 }
417 should_rotate
418}
419
420#[derive(Default)]
427struct RecipientDevicesForUser {
428 allowed_devices: Vec<DeviceData>,
430 denied_devices_with_code: Vec<(DeviceData, WithheldCode)>,
432}
433
434enum ErrorOnVerifiedUserProblemResult {
437 UnsignedDevicesOfVerifiedUser(Vec<OwnedDeviceId>),
441
442 Devices(RecipientDevicesForUser),
444}
445
446fn split_devices_for_user_for_all_devices_strategy(
449 user_devices: HashMap<OwnedDeviceId, DeviceData>,
450 own_identity: &Option<OwnUserIdentityData>,
451 device_owner_identity: &Option<UserIdentityData>,
452) -> RecipientDevicesForUser {
453 let (left, right) = user_devices.into_values().partition_map(|d| {
454 if d.is_blacklisted() {
455 Either::Right((d, WithheldCode::Blacklisted))
456 } else if d.is_dehydrated()
457 && should_withhold_to_dehydrated_device(
458 &d,
459 own_identity.as_ref(),
460 device_owner_identity.as_ref(),
461 )
462 {
463 Either::Right((d, WithheldCode::Unverified))
464 } else {
465 Either::Left(d)
466 }
467 });
468
469 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
470}
471
472fn should_withhold_to_dehydrated_device(
481 device: &DeviceData,
482 own_identity: Option<&OwnUserIdentityData>,
483 device_owner_identity: Option<&UserIdentityData>,
484) -> bool {
485 device_owner_identity.is_none_or(|owner_id| {
486 !device.is_cross_signed_by_owner(owner_id) ||
488
489 (owner_id.was_previously_verified() && !is_user_verified(own_identity, owner_id))
491 })
492}
493
494fn split_devices_for_user_for_error_on_verified_user_problem_strategy(
507 user_devices: HashMap<OwnedDeviceId, DeviceData>,
508 own_identity: &Option<OwnUserIdentityData>,
509 device_owner_identity: &Option<UserIdentityData>,
510) -> ErrorOnVerifiedUserProblemResult {
511 let mut recipient_devices = RecipientDevicesForUser::default();
512
513 let mut unsigned_devices_of_verified_users: Option<Vec<OwnedDeviceId>> = None;
516
517 for d in user_devices.into_values() {
518 match handle_device_for_user_for_error_on_verified_user_problem_strategy(
519 &d,
520 own_identity.as_ref(),
521 device_owner_identity.as_ref(),
522 ) {
523 ErrorOnVerifiedUserProblemDeviceDecision::Ok => {
524 recipient_devices.allowed_devices.push(d)
525 }
526 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => {
527 recipient_devices.denied_devices_with_code.push((d, code))
528 }
529 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
530 unsigned_devices_of_verified_users
531 .get_or_insert_with(Vec::default)
532 .push(d.device_id().to_owned())
533 }
534 }
535 }
536
537 if let Some(devices) = unsigned_devices_of_verified_users {
538 ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices)
539 } else {
540 ErrorOnVerifiedUserProblemResult::Devices(recipient_devices)
541 }
542}
543
544enum ErrorOnVerifiedUserProblemDeviceDecision {
547 Ok,
548 Withhold(WithheldCode),
549 UnsignedOfVerified,
550}
551
552fn handle_device_for_user_for_error_on_verified_user_problem_strategy(
553 device: &DeviceData,
554 own_identity: Option<&OwnUserIdentityData>,
555 device_owner_identity: Option<&UserIdentityData>,
556) -> ErrorOnVerifiedUserProblemDeviceDecision {
557 if device.is_blacklisted() {
558 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Blacklisted)
559 } else if device.local_trust_state() == LocalTrust::Ignored {
560 ErrorOnVerifiedUserProblemDeviceDecision::Ok
562 } else if is_unsigned_device_of_verified_user(own_identity, device_owner_identity, device) {
563 ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified
564 } else if device.is_dehydrated()
565 && device_owner_identity.is_none_or(|owner_id| {
566 !device.is_cross_signed_by_owner(owner_id)
569 })
570 {
571 ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Unverified)
572 } else {
573 ErrorOnVerifiedUserProblemDeviceDecision::Ok
574 }
575}
576
577fn split_devices_for_user_for_identity_based_strategy(
578 user_devices: HashMap<OwnedDeviceId, DeviceData>,
579 device_owner_identity: &Option<UserIdentityData>,
580) -> RecipientDevicesForUser {
581 match device_owner_identity {
582 None => {
583 RecipientDevicesForUser {
586 allowed_devices: Vec::default(),
587 denied_devices_with_code: user_devices
588 .into_values()
589 .map(|d| (d, WithheldCode::Unverified))
590 .collect(),
591 }
592 }
593 Some(device_owner_identity) => {
594 let (recipients, withheld_recipients): (
596 Vec<DeviceData>,
597 Vec<(DeviceData, WithheldCode)>,
598 ) = user_devices.into_values().partition_map(|d| {
599 if d.is_cross_signed_by_owner(device_owner_identity) {
600 Either::Left(d)
601 } else {
602 Either::Right((d, WithheldCode::Unverified))
603 }
604 });
605 RecipientDevicesForUser {
606 allowed_devices: recipients,
607 denied_devices_with_code: withheld_recipients,
608 }
609 }
610 }
611}
612
613fn split_devices_for_user_for_only_trusted_devices(
616 user_devices: HashMap<OwnedDeviceId, DeviceData>,
617 own_identity: &Option<OwnUserIdentityData>,
618 device_owner_identity: &Option<UserIdentityData>,
619) -> RecipientDevicesForUser {
620 let (left, right) = user_devices.into_values().partition_map(|d| {
621 match (
622 d.local_trust_state(),
623 d.is_cross_signing_trusted(own_identity, device_owner_identity),
624 ) {
625 (LocalTrust::BlackListed, _) => Either::Right((d, WithheldCode::Blacklisted)),
626 (LocalTrust::Ignored | LocalTrust::Verified, _) => Either::Left(d),
627 (LocalTrust::Unset, false) => Either::Right((d, WithheldCode::Unverified)),
628 (LocalTrust::Unset, true) => Either::Left(d),
629 }
630 });
631 RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
632}
633
634fn is_unsigned_device_of_verified_user(
635 own_identity: Option<&OwnUserIdentityData>,
636 device_owner_identity: Option<&UserIdentityData>,
637 device_data: &DeviceData,
638) -> bool {
639 device_owner_identity.is_some_and(|device_owner_identity| {
640 is_user_verified(own_identity, device_owner_identity)
641 && !device_data.is_cross_signed_by_owner(device_owner_identity)
642 })
643}
644
645fn has_identity_verification_violation(
652 own_identity: Option<&OwnUserIdentityData>,
653 device_owner_identity: Option<&UserIdentityData>,
654) -> bool {
655 device_owner_identity.is_some_and(|device_owner_identity| {
656 device_owner_identity.was_previously_verified()
657 && !is_user_verified(own_identity, device_owner_identity)
658 })
659}
660
661fn is_user_verified(
662 own_identity: Option<&OwnUserIdentityData>,
663 user_identity: &UserIdentityData,
664) -> bool {
665 match user_identity {
666 UserIdentityData::Own(own_identity) => own_identity.is_verified(),
667 UserIdentityData::Other(other_identity) => {
668 own_identity.is_some_and(|oi| oi.is_identity_verified(other_identity))
669 }
670 }
671}
672
673#[cfg(test)]
674mod tests {
675 use std::{collections::BTreeMap, iter, sync::Arc};
676
677 use assert_matches::assert_matches;
678 use assert_matches2::assert_let;
679 use insta::{assert_snapshot, with_settings};
680 use matrix_sdk_common::deserialized_responses::WithheldCode;
681 use matrix_sdk_test::{
682 async_test, test_json,
683 test_json::keys_query_sets::{
684 IdentityChangeDataSet, KeyDistributionTestData, MaloIdentityChangeDataSet,
685 VerificationViolationTestData,
686 },
687 };
688 use ruma::{
689 device_id, events::room::history_visibility::HistoryVisibility, room_id, TransactionId,
690 };
691 use serde_json::json;
692
693 use crate::{
694 error::SessionRecipientCollectionError,
695 olm::OutboundGroupSession,
696 session_manager::{
697 group_sessions::share_strategy::collect_session_recipients, CollectStrategy,
698 },
699 testing::simulate_key_query_response_for_verification,
700 CrossSigningKeyExport, EncryptionSettings, LocalTrust, OlmError, OlmMachine,
701 };
702
703 async fn test_machine() -> OlmMachine {
707 use KeyDistributionTestData as DataSet;
708
709 let machine = OlmMachine::new(DataSet::me_id(), DataSet::me_device_id()).await;
711 let keys_query = DataSet::me_keys_query_response();
712 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
713
714 machine
716 .import_cross_signing_keys(CrossSigningKeyExport {
717 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
718 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
719 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
720 })
721 .await
722 .unwrap();
723
724 machine
725 }
726
727 async fn import_known_users_to_test_machine(machine: &OlmMachine) {
730 let keys_query = KeyDistributionTestData::dan_keys_query_response();
731 let txn_id = TransactionId::new();
732 machine.mark_request_as_sent(&txn_id, &keys_query).await.unwrap();
733
734 let txn_id_dave = TransactionId::new();
735 let keys_query_dave = KeyDistributionTestData::dave_keys_query_response();
736 machine.mark_request_as_sent(&txn_id_dave, &keys_query_dave).await.unwrap();
737
738 let txn_id_good = TransactionId::new();
739 let keys_query_good = KeyDistributionTestData::good_keys_query_response();
740 machine.mark_request_as_sent(&txn_id_good, &keys_query_good).await.unwrap();
741 }
742
743 #[test]
746 fn test_serialize_device_based_strategy() {
747 let encryption_settings = all_devices_strategy_settings();
748 let serialized = serde_json::to_string(&encryption_settings).unwrap();
749 with_settings!({prepend_module_to_snapshot => false}, {
750 assert_snapshot!(serialized)
751 });
752 }
753
754 #[test]
758 fn test_deserialize_old_device_based_strategy() {
759 let settings: EncryptionSettings = serde_json::from_value(json!({
760 "algorithm": "m.megolm.v1.aes-sha2",
761 "rotation_period":{"secs":604800,"nanos":0},
762 "rotation_period_msgs":100,
763 "history_visibility":"shared",
764 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":false}},
765 })).unwrap();
766 assert_matches!(settings.sharing_strategy, CollectStrategy::AllDevices);
767 }
768
769 #[test]
773 fn test_deserialize_old_error_on_verified_user_problem() {
774 let settings: EncryptionSettings = serde_json::from_value(json!({
775 "algorithm": "m.megolm.v1.aes-sha2",
776 "rotation_period":{"secs":604800,"nanos":0},
777 "rotation_period_msgs":100,
778 "history_visibility":"shared",
779 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":true}},
780 })).unwrap();
781 assert_matches!(settings.sharing_strategy, CollectStrategy::ErrorOnVerifiedUserProblem);
782 }
783
784 #[test]
788 fn test_deserialize_old_only_trusted_devices_strategy() {
789 let settings: EncryptionSettings = serde_json::from_value(json!({
790 "algorithm": "m.megolm.v1.aes-sha2",
791 "rotation_period":{"secs":604800,"nanos":0},
792 "rotation_period_msgs":100,
793 "history_visibility":"shared",
794 "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":true,"error_on_verified_user_problem":false}},
795 })).unwrap();
796 assert_matches!(settings.sharing_strategy, CollectStrategy::OnlyTrustedDevices);
797 }
798
799 #[async_test]
800 async fn test_share_with_per_device_strategy_to_all() {
801 let machine = test_machine().await;
802 import_known_users_to_test_machine(&machine).await;
803
804 let encryption_settings = all_devices_strategy_settings();
805
806 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
807
808 let share_result = collect_session_recipients(
809 machine.store(),
810 vec![
811 KeyDistributionTestData::dan_id(),
812 KeyDistributionTestData::dave_id(),
813 KeyDistributionTestData::good_id(),
814 ]
815 .into_iter(),
816 &encryption_settings,
817 &group_session,
818 )
819 .await
820 .unwrap();
821
822 assert!(!share_result.should_rotate);
823
824 let dan_devices_shared =
825 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
826 let dave_devices_shared =
827 share_result.devices.get(KeyDistributionTestData::dave_id()).unwrap();
828 let good_devices_shared =
829 share_result.devices.get(KeyDistributionTestData::good_id()).unwrap();
830
831 assert_eq!(dan_devices_shared.len(), 2);
833 assert_eq!(dave_devices_shared.len(), 1);
834 assert_eq!(good_devices_shared.len(), 2);
835 }
836
837 #[async_test]
838 async fn test_share_with_only_trusted_strategy() {
839 let machine = test_machine().await;
840 import_known_users_to_test_machine(&machine).await;
841
842 let encryption_settings = EncryptionSettings {
843 sharing_strategy: CollectStrategy::OnlyTrustedDevices,
844 ..Default::default()
845 };
846
847 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
848
849 let share_result = collect_session_recipients(
850 machine.store(),
851 vec![
852 KeyDistributionTestData::dan_id(),
853 KeyDistributionTestData::dave_id(),
854 KeyDistributionTestData::good_id(),
855 ]
856 .into_iter(),
857 &encryption_settings,
858 &group_session,
859 )
860 .await
861 .unwrap();
862
863 assert!(!share_result.should_rotate);
864
865 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
866 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
867 assert!(dave_devices_shared.unwrap().is_empty());
869 assert!(good_devices_shared.unwrap().is_empty());
870
871 let dan_devices_shared =
874 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
875
876 assert_eq!(dan_devices_shared.len(), 1);
877 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
878 assert_eq!(
879 dan_device_that_will_get_the_key.device_id().as_str(),
880 KeyDistributionTestData::dan_signed_device_id()
881 );
882
883 let (_, code) = share_result
885 .withheld_devices
886 .iter()
887 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
888 .expect("This dan's device should receive a withheld code");
889
890 assert_eq!(code, &WithheldCode::Unverified);
891
892 let (_, code) = share_result
893 .withheld_devices
894 .iter()
895 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
896 .expect("This daves's device should receive a withheld code");
897
898 assert_eq!(code, &WithheldCode::Unverified);
899 }
900
901 #[async_test]
905 async fn test_error_on_unsigned_of_verified_users() {
906 use VerificationViolationTestData as DataSet;
907
908 let machine = unsigned_of_verified_setup().await;
910
911 let carol_keys = DataSet::carol_keys_query_response_signed();
913 machine.mark_request_as_sent(&TransactionId::new(), &carol_keys).await.unwrap();
914
915 let carol_identity =
917 machine.get_identity(DataSet::carol_id(), None).await.unwrap().unwrap();
918 assert!(carol_identity.other().unwrap().is_verified());
919
920 let carol_unsigned_device = machine
921 .get_device(DataSet::carol_id(), DataSet::carol_unsigned_device_id(), None)
922 .await
923 .unwrap()
924 .unwrap();
925 assert!(!carol_unsigned_device.is_verified());
926
927 let encryption_settings = error_on_verification_problem_encryption_settings();
929 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
930 let share_result = collect_session_recipients(
931 machine.store(),
932 vec![DataSet::bob_id(), DataSet::carol_id()].into_iter(),
933 &encryption_settings,
934 &group_session,
935 )
936 .await;
937
938 assert_let!(
939 Err(OlmError::SessionRecipientCollectionError(
940 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
941 )) = share_result
942 );
943
944 assert_eq!(
946 unverified_devices,
947 BTreeMap::from([
948 (DataSet::bob_id().to_owned(), vec![DataSet::bob_device_2_id().to_owned()]),
949 (
950 DataSet::carol_id().to_owned(),
951 vec![DataSet::carol_unsigned_device_id().to_owned()]
952 ),
953 ])
954 );
955 }
956
957 #[async_test]
961 async fn test_error_on_unsigned_of_verified_resolve_by_whitelisting() {
962 use VerificationViolationTestData as DataSet;
963
964 let machine = unsigned_of_verified_setup().await;
965
966 machine
968 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
969 .await
970 .unwrap()
971 .unwrap()
972 .set_local_trust(LocalTrust::Ignored)
973 .await
974 .unwrap();
975
976 let encryption_settings = error_on_verification_problem_encryption_settings();
977 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
978
979 let share_result = collect_session_recipients(
981 machine.store(),
982 iter::once(DataSet::bob_id()),
983 &encryption_settings,
984 &group_session,
985 )
986 .await
987 .unwrap();
988
989 assert_eq!(2, share_result.devices.get(DataSet::bob_id()).unwrap().len());
990 assert_eq!(0, share_result.withheld_devices.len());
991 }
992
993 #[async_test]
997 async fn test_error_on_unsigned_of_verified_resolve_by_blacklisting() {
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::BlackListed)
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!(1, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1026 let withheld_list: Vec<_> = share_result
1027 .withheld_devices
1028 .iter()
1029 .map(|(d, code)| (d.device_id().to_owned(), code.clone()))
1030 .collect();
1031 assert_eq!(
1032 withheld_list,
1033 vec![(DataSet::bob_device_2_id().to_owned(), WithheldCode::Blacklisted)]
1034 );
1035 }
1036
1037 #[async_test]
1041 async fn test_error_on_unsigned_of_verified_owner_is_us() {
1042 use VerificationViolationTestData as DataSet;
1043
1044 let machine = unsigned_of_verified_setup().await;
1045
1046 let mut own_keys = DataSet::own_keys_query_response_1().clone();
1048 own_keys.device_keys.insert(
1049 DataSet::own_id().to_owned(),
1050 BTreeMap::from([
1051 DataSet::own_signed_device_keys(),
1052 DataSet::own_unsigned_device_keys(),
1053 ]),
1054 );
1055 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1056
1057 let encryption_settings = error_on_verification_problem_encryption_settings();
1058 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1059 let share_result = collect_session_recipients(
1060 machine.store(),
1061 iter::once(DataSet::own_id()),
1062 &encryption_settings,
1063 &group_session,
1064 )
1065 .await;
1066
1067 assert_let!(
1068 Err(OlmError::SessionRecipientCollectionError(
1069 SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
1070 )) = share_result
1071 );
1072
1073 assert_eq!(
1075 unverified_devices,
1076 BTreeMap::from([(
1077 DataSet::own_id().to_owned(),
1078 vec![DataSet::own_unsigned_device_id()]
1079 ),])
1080 );
1081 }
1082
1083 #[async_test]
1086 async fn test_should_not_error_on_unsigned_of_unverified() {
1087 use VerificationViolationTestData as DataSet;
1088
1089 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1090
1091 let own_keys = DataSet::own_keys_query_response_1();
1093 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1094
1095 machine
1097 .import_cross_signing_keys(CrossSigningKeyExport {
1098 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1099 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1100 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1101 })
1102 .await
1103 .unwrap();
1104
1105 let bob_keys = DataSet::bob_keys_query_response_rotated();
1107 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1108
1109 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1112 assert!(!bob_identity.other().unwrap().is_verified());
1113
1114 let bob_unsigned_device = machine
1115 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
1116 .await
1117 .unwrap()
1118 .unwrap();
1119 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1120
1121 let encryption_settings = error_on_verification_problem_encryption_settings();
1122 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1123 collect_session_recipients(
1124 machine.store(),
1125 iter::once(DataSet::bob_id()),
1126 &encryption_settings,
1127 &group_session,
1128 )
1129 .await
1130 .unwrap();
1131 }
1132
1133 #[async_test]
1136 async fn test_should_not_error_on_unsigned_of_signed_but_unverified() {
1137 use VerificationViolationTestData as DataSet;
1138
1139 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1140
1141 let keys_query = DataSet::own_keys_query_response_1();
1143 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1144
1145 let keys_query = DataSet::bob_keys_query_response_signed();
1147 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1148
1149 let bob_identity =
1152 machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap().other().unwrap();
1153 assert!(bob_identity
1154 .own_identity
1155 .as_ref()
1156 .unwrap()
1157 .is_identity_signed(&bob_identity.inner));
1158 assert!(!bob_identity.is_verified());
1159
1160 let bob_unsigned_device = machine
1161 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1162 .await
1163 .unwrap()
1164 .unwrap();
1165 assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1166
1167 let encryption_settings = error_on_verification_problem_encryption_settings();
1169 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1170 collect_session_recipients(
1171 machine.store(),
1172 iter::once(DataSet::bob_id()),
1173 &encryption_settings,
1174 &group_session,
1175 )
1176 .await
1177 .unwrap();
1178 }
1179
1180 #[async_test]
1184 async fn test_verified_user_changed_identity() {
1185 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1186
1187 let machine = unsigned_of_verified_setup().await;
1190
1191 let bob_keys = DataSet::bob_keys_query_response_rotated();
1193 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1194
1195 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1197 assert!(bob_identity.has_verification_violation());
1198
1199 let encryption_settings = error_on_verification_problem_encryption_settings();
1201 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1202 let share_result = collect_session_recipients(
1203 machine.store(),
1204 iter::once(DataSet::bob_id()),
1205 &encryption_settings,
1206 &group_session,
1207 )
1208 .await;
1209
1210 assert_let!(
1211 Err(OlmError::SessionRecipientCollectionError(
1212 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1213 )) = share_result
1214 );
1215 assert_eq!(violating_users, vec![DataSet::bob_id()]);
1216
1217 bob_identity.withdraw_verification().await.unwrap();
1219
1220 collect_session_recipients(
1221 machine.store(),
1222 iter::once(DataSet::bob_id()),
1223 &encryption_settings,
1224 &group_session,
1225 )
1226 .await
1227 .unwrap();
1228 }
1229
1230 #[async_test]
1234 async fn test_own_verified_identity_changed() {
1235 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
1236
1237 let machine = unsigned_of_verified_setup().await;
1239 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1240 assert!(own_identity.own().unwrap().is_verified());
1241
1242 let own_keys = DataSet::own_keys_query_response_2();
1244 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1245
1246 let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
1247 assert!(!own_identity.is_verified());
1248
1249 let encryption_settings = error_on_verification_problem_encryption_settings();
1251 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1252 let share_result = collect_session_recipients(
1253 machine.store(),
1254 iter::once(DataSet::own_id()),
1255 &encryption_settings,
1256 &group_session,
1257 )
1258 .await;
1259
1260 assert_let!(
1261 Err(OlmError::SessionRecipientCollectionError(
1262 SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
1263 )) = share_result
1264 );
1265 assert_eq!(violating_users, vec![DataSet::own_id()]);
1266
1267 own_identity.withdraw_verification().await.unwrap();
1269
1270 collect_session_recipients(
1271 machine.store(),
1272 iter::once(DataSet::own_id()),
1273 &encryption_settings,
1274 &group_session,
1275 )
1276 .await
1277 .unwrap();
1278 }
1279
1280 mod dehydrated_device {
1283 use std::{collections::HashSet, iter};
1284
1285 use insta::{allow_duplicates, assert_json_snapshot, with_settings};
1286 use matrix_sdk_common::deserialized_responses::WithheldCode;
1287 use matrix_sdk_test::{
1288 async_test, ruma_response_to_json,
1289 test_json::keys_query_sets::{
1290 KeyDistributionTestData, KeyQueryResponseTemplate,
1291 KeyQueryResponseTemplateDeviceOptions,
1292 },
1293 };
1294 use ruma::{device_id, user_id, DeviceId, TransactionId, UserId};
1295 use vodozemac::{Curve25519PublicKey, Ed25519SecretKey};
1296
1297 use super::{
1298 all_devices_strategy_settings, create_test_outbound_group_session,
1299 error_on_verification_problem_encryption_settings, identity_based_strategy_settings,
1300 test_machine,
1301 };
1302 use crate::{
1303 session_manager::group_sessions::{
1304 share_strategy::collect_session_recipients, CollectRecipientsResult,
1305 },
1306 EncryptionSettings, OlmMachine,
1307 };
1308
1309 #[async_test]
1310 async fn test_all_devices_strategy_should_share_with_verified_dehydrated_device() {
1311 should_share_with_verified_dehydrated_device(&all_devices_strategy_settings()).await
1312 }
1313
1314 #[async_test]
1315 async fn test_error_on_verification_problem_strategy_should_share_with_verified_dehydrated_device(
1316 ) {
1317 should_share_with_verified_dehydrated_device(
1318 &error_on_verification_problem_encryption_settings(),
1319 )
1320 .await
1321 }
1322
1323 #[async_test]
1324 async fn test_identity_based_strategy_should_share_with_verified_dehydrated_device() {
1325 should_share_with_verified_dehydrated_device(&identity_based_strategy_settings()).await
1326 }
1327
1328 async fn should_share_with_verified_dehydrated_device(
1333 encryption_settings: &EncryptionSettings,
1334 ) {
1335 let machine = test_machine().await;
1336
1337 let bob_user_id = user_id!("@bob:localhost");
1340 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1341 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1342 .with_dehydrated_device(bob_dehydrated_device_id, true)
1343 .build_response();
1344 allow_duplicates! {
1345 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1346 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1347 });
1348 }
1349 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1350
1351 let recips = share_test_session_and_collect_recipients(
1353 &machine,
1354 bob_user_id,
1355 encryption_settings,
1356 )
1357 .await;
1358
1359 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1361 }
1362
1363 #[async_test]
1364 async fn test_all_devices_strategy_should_not_share_with_unverified_dehydrated_device() {
1365 should_not_share_with_unverified_dehydrated_device(&all_devices_strategy_settings())
1366 .await
1367 }
1368
1369 #[async_test]
1370 async fn test_error_on_verification_problem_strategy_should_not_share_with_unverified_dehydrated_device(
1371 ) {
1372 should_not_share_with_unverified_dehydrated_device(
1373 &error_on_verification_problem_encryption_settings(),
1374 )
1375 .await
1376 }
1377
1378 #[async_test]
1379 async fn test_identity_based_strategy_should_not_share_with_unverified_dehydrated_device() {
1380 should_not_share_with_unverified_dehydrated_device(&identity_based_strategy_settings())
1381 .await
1382 }
1383
1384 async fn should_not_share_with_unverified_dehydrated_device(
1389 encryption_settings: &EncryptionSettings,
1390 ) {
1391 let machine = test_machine().await;
1392
1393 let bob_user_id = user_id!("@bob:localhost");
1396 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1397 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1398 .with_dehydrated_device(bob_dehydrated_device_id, false)
1399 .build_response();
1400 allow_duplicates! {
1401 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1402 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1403 });
1404 }
1405 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1406
1407 let recips = share_test_session_and_collect_recipients(
1409 &machine,
1410 bob_user_id,
1411 encryption_settings,
1412 )
1413 .await;
1414
1415 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1418 }
1419
1420 #[async_test]
1421 async fn test_all_devices_strategy_should_share_with_verified_device_of_pin_violation_user()
1422 {
1423 should_share_with_verified_device_of_pin_violation_user(
1424 &all_devices_strategy_settings(),
1425 )
1426 .await
1427 }
1428
1429 #[async_test]
1430 async fn test_error_on_verification_problem_strategy_should_share_with_verified_device_of_pin_violation_user(
1431 ) {
1432 should_share_with_verified_device_of_pin_violation_user(
1433 &error_on_verification_problem_encryption_settings(),
1434 )
1435 .await
1436 }
1437
1438 #[async_test]
1439 async fn test_identity_based_strategy_should_share_with_verified_device_of_pin_violation_user(
1440 ) {
1441 should_share_with_verified_device_of_pin_violation_user(
1442 &identity_based_strategy_settings(),
1443 )
1444 .await
1445 }
1446
1447 async fn should_share_with_verified_device_of_pin_violation_user(
1452 encryption_settings: &EncryptionSettings,
1453 ) {
1454 let machine = test_machine().await;
1455
1456 let bob_user_id = user_id!("@bob:localhost");
1458 let keys_query =
1459 key_query_response_template_with_cross_signing(bob_user_id).build_response();
1460 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1461
1462 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1465 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1466 .with_dehydrated_device(bob_dehydrated_device_id, true)
1467 .build_response();
1468 allow_duplicates! {
1469 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1470 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1471 });
1472 }
1473 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1474
1475 let recips = share_test_session_and_collect_recipients(
1477 &machine,
1478 bob_user_id,
1479 encryption_settings,
1480 )
1481 .await;
1482
1483 assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
1485 }
1486
1487 #[async_test]
1488 async fn test_all_devices_strategy_should_not_share_with_dehydrated_device_of_verification_violation_user(
1489 ) {
1490 should_not_share_with_dehydrated_device_of_verification_violation_user(
1491 &all_devices_strategy_settings(),
1492 )
1493 .await
1494 }
1495
1496 async fn should_not_share_with_dehydrated_device_of_verification_violation_user(
1499 encryption_settings: &EncryptionSettings,
1500 ) {
1501 let bob_user_id = user_id!("@bob:localhost");
1502 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1503 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1504 bob_user_id,
1505 bob_dehydrated_device_id,
1506 )
1507 .await;
1508
1509 let recips = share_test_session_and_collect_recipients(
1511 &machine,
1512 bob_user_id,
1513 encryption_settings,
1514 )
1515 .await;
1516
1517 assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
1520 }
1521
1522 #[async_test]
1523 async fn test_error_on_verification_problem_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1524 ) {
1525 should_give_error_for_dehydrated_device_of_verification_violation_user(
1526 &error_on_verification_problem_encryption_settings(),
1527 )
1528 .await
1529 }
1530
1531 #[async_test]
1532 async fn test_identity_based_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user(
1533 ) {
1534 should_give_error_for_dehydrated_device_of_verification_violation_user(
1538 &identity_based_strategy_settings(),
1539 )
1540 .await
1541 }
1542
1543 async fn should_give_error_for_dehydrated_device_of_verification_violation_user(
1547 encryption_settings: &EncryptionSettings,
1548 ) {
1549 let bob_user_id = user_id!("@bob:localhost");
1550 let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
1551 let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
1552 bob_user_id,
1553 bob_dehydrated_device_id,
1554 )
1555 .await;
1556
1557 let group_session = create_test_outbound_group_session(&machine, encryption_settings);
1558 let share_result = collect_session_recipients(
1559 machine.store(),
1560 iter::once(bob_user_id),
1561 encryption_settings,
1562 &group_session,
1563 )
1564 .await;
1565
1566 assert_matches::assert_matches!(
1569 share_result,
1570 Err(crate::OlmError::SessionRecipientCollectionError(
1571 crate::SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
1572 ))
1573 );
1574 }
1575
1576 async fn prepare_machine_with_dehydrated_device_of_verification_violation_user(
1580 bob_user_id: &UserId,
1581 bob_dehydrated_device_id: &DeviceId,
1582 ) -> OlmMachine {
1583 let machine = test_machine().await;
1584
1585 let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
1587 .with_user_verification_signature(
1588 KeyDistributionTestData::me_id(),
1589 &KeyDistributionTestData::me_private_user_signing_key(),
1590 )
1591 .build_response();
1592 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1593
1594 let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
1597 .with_dehydrated_device(bob_dehydrated_device_id, true)
1598 .build_response();
1599 allow_duplicates! {
1600 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1601 assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
1602 });
1603 }
1604 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1605
1606 machine
1607 }
1608
1609 async fn share_test_session_and_collect_recipients(
1612 machine: &OlmMachine,
1613 target_user_id: &UserId,
1614 encryption_settings: &EncryptionSettings,
1615 ) -> CollectRecipientsResult {
1616 let group_session = create_test_outbound_group_session(machine, encryption_settings);
1617 collect_session_recipients(
1618 machine.store(),
1619 iter::once(target_user_id),
1620 encryption_settings,
1621 &group_session,
1622 )
1623 .await
1624 .unwrap()
1625 }
1626
1627 fn assert_shared_with(
1630 recips: CollectRecipientsResult,
1631 user_id: &UserId,
1632 device_ids: HashSet<&DeviceId>,
1633 ) {
1634 let bob_devices_shared: HashSet<_> = recips
1635 .devices
1636 .get(user_id)
1637 .unwrap_or_else(|| panic!("session not shared with {user_id}"))
1638 .iter()
1639 .map(|d| d.device_id())
1640 .collect();
1641 assert_eq!(bob_devices_shared, device_ids);
1642
1643 assert!(recips.withheld_devices.is_empty(), "Unexpected withheld messages");
1644 }
1645
1646 fn assert_withheld_to(
1649 recips: CollectRecipientsResult,
1650 bob_user_id: &UserId,
1651 bob_dehydrated_device_id: &DeviceId,
1652 ) {
1653 for (user, device_list) in recips.devices {
1655 assert_eq!(device_list.len(), 0, "session unexpectedly shared with {}", user);
1656 }
1657
1658 assert_eq!(recips.withheld_devices.len(), 1);
1660 assert_eq!(recips.withheld_devices[0].0.user_id(), bob_user_id);
1661 assert_eq!(recips.withheld_devices[0].0.device_id(), bob_dehydrated_device_id);
1662 assert_eq!(recips.withheld_devices[0].1, WithheldCode::Unverified);
1663 }
1664
1665 fn key_query_response_template_with_cross_signing(
1668 user_id: &UserId,
1669 ) -> KeyQueryResponseTemplate {
1670 KeyQueryResponseTemplate::new(user_id.to_owned()).with_cross_signing_keys(
1671 Ed25519SecretKey::from_slice(b"master12master12master12master12"),
1672 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1673 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1674 )
1675 }
1676
1677 fn key_query_response_template_with_changed_cross_signing(
1681 bob_user_id: &UserId,
1682 ) -> KeyQueryResponseTemplate {
1683 KeyQueryResponseTemplate::new(bob_user_id.to_owned()).with_cross_signing_keys(
1684 Ed25519SecretKey::from_slice(b"newmaster__newmaster__newmaster_"),
1685 Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
1686 Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
1687 )
1688 }
1689
1690 trait KeyQueryResponseTemplateExt {
1691 fn with_dehydrated_device(
1692 self,
1693 device_id: &DeviceId,
1694 verified: bool,
1695 ) -> KeyQueryResponseTemplate;
1696 }
1697
1698 impl KeyQueryResponseTemplateExt for KeyQueryResponseTemplate {
1699 fn with_dehydrated_device(
1701 self,
1702 device_id: &DeviceId,
1703 verified: bool,
1704 ) -> KeyQueryResponseTemplate {
1705 self.with_device(
1706 device_id,
1707 &Curve25519PublicKey::from(b"curvepubcurvepubcurvepubcurvepub".to_owned()),
1708 &Ed25519SecretKey::from_slice(b"device12device12device12device12"),
1709 KeyQueryResponseTemplateDeviceOptions::new()
1710 .dehydrated(true)
1711 .verified(verified),
1712 )
1713 }
1714 }
1715 }
1716
1717 #[async_test]
1718 async fn test_share_with_identity_strategy() {
1719 let machine = test_machine().await;
1720 import_known_users_to_test_machine(&machine).await;
1721
1722 let encryption_settings = identity_based_strategy_settings();
1723
1724 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1725
1726 let share_result = collect_session_recipients(
1727 machine.store(),
1728 vec![
1729 KeyDistributionTestData::dan_id(),
1730 KeyDistributionTestData::dave_id(),
1731 KeyDistributionTestData::good_id(),
1732 ]
1733 .into_iter(),
1734 &encryption_settings,
1735 &group_session,
1736 )
1737 .await
1738 .unwrap();
1739
1740 assert!(!share_result.should_rotate);
1741
1742 let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
1743 let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
1744 assert!(dave_devices_shared.unwrap().is_empty());
1746
1747 assert_eq!(good_devices_shared.unwrap().len(), 2);
1749
1750 let dan_devices_shared =
1753 share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
1754
1755 assert_eq!(dan_devices_shared.len(), 1);
1756 let dan_device_that_will_get_the_key = &dan_devices_shared[0];
1757 assert_eq!(
1758 dan_device_that_will_get_the_key.device_id().as_str(),
1759 KeyDistributionTestData::dan_signed_device_id()
1760 );
1761
1762 let (_, code) = share_result
1764 .withheld_devices
1765 .iter()
1766 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
1767 .expect("This dan's device should receive a withheld code");
1768
1769 assert_eq!(code, &WithheldCode::Unverified);
1770
1771 let (_, code) = share_result
1773 .withheld_devices
1774 .iter()
1775 .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
1776 .expect("This dave device should receive a withheld code");
1777
1778 assert_eq!(code, &WithheldCode::Unverified);
1779 }
1780
1781 #[async_test]
1784 async fn test_share_identity_strategy_no_cross_signing() {
1785 let machine: OlmMachine = OlmMachine::new(
1788 KeyDistributionTestData::me_id(),
1789 KeyDistributionTestData::me_device_id(),
1790 )
1791 .await;
1792
1793 let keys_query = KeyDistributionTestData::dan_keys_query_response();
1794 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1795
1796 let fake_room_id = room_id!("!roomid:localhost");
1797
1798 let encryption_settings = identity_based_strategy_settings();
1799
1800 let request_result = machine
1801 .share_room_key(
1802 fake_room_id,
1803 iter::once(KeyDistributionTestData::dan_id()),
1804 encryption_settings.clone(),
1805 )
1806 .await;
1807
1808 assert_matches!(
1809 request_result,
1810 Err(OlmError::SessionRecipientCollectionError(
1811 SessionRecipientCollectionError::CrossSigningNotSetup
1812 ))
1813 );
1814
1815 let keys_query = KeyDistributionTestData::me_keys_query_response();
1819 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1820
1821 let request_result = machine
1822 .share_room_key(
1823 fake_room_id,
1824 iter::once(KeyDistributionTestData::dan_id()),
1825 encryption_settings.clone(),
1826 )
1827 .await;
1828
1829 assert_matches!(
1830 request_result,
1831 Err(OlmError::SessionRecipientCollectionError(
1832 SessionRecipientCollectionError::SendingFromUnverifiedDevice
1833 ))
1834 );
1835
1836 machine
1839 .import_cross_signing_keys(CrossSigningKeyExport {
1840 master_key: KeyDistributionTestData::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1841 self_signing_key: KeyDistributionTestData::SELF_SIGNING_KEY_PRIVATE_EXPORT
1842 .to_owned()
1843 .into(),
1844 user_signing_key: KeyDistributionTestData::USER_SIGNING_KEY_PRIVATE_EXPORT
1845 .to_owned()
1846 .into(),
1847 })
1848 .await
1849 .unwrap();
1850
1851 let requests = machine
1852 .share_room_key(
1853 fake_room_id,
1854 iter::once(KeyDistributionTestData::dan_id()),
1855 encryption_settings.clone(),
1856 )
1857 .await
1858 .unwrap();
1859
1860 assert_eq!(requests.len(), 1);
1863 }
1864
1865 #[async_test]
1869 async fn test_share_identity_strategy_report_verification_violation() {
1870 let machine: OlmMachine = OlmMachine::new(
1871 KeyDistributionTestData::me_id(),
1872 KeyDistributionTestData::me_device_id(),
1873 )
1874 .await;
1875
1876 machine.bootstrap_cross_signing(false).await.unwrap();
1877
1878 let user1 = IdentityChangeDataSet::user_id();
1880 let user2 = MaloIdentityChangeDataSet::user_id();
1881
1882 let keys_query = IdentityChangeDataSet::key_query_with_identity_a();
1884 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1885
1886 let keys_query = MaloIdentityChangeDataSet::initial_key_query();
1887 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1888
1889 let keys_query = IdentityChangeDataSet::key_query_with_identity_b();
1893 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1894 machine
1895 .get_identity(user1, None)
1896 .await
1897 .unwrap()
1898 .unwrap()
1899 .other()
1900 .unwrap()
1901 .mark_as_previously_verified()
1902 .await
1903 .unwrap();
1904
1905 let keys_query = MaloIdentityChangeDataSet::updated_key_query();
1906 machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1907 machine
1908 .get_identity(user2, None)
1909 .await
1910 .unwrap()
1911 .unwrap()
1912 .other()
1913 .unwrap()
1914 .mark_as_previously_verified()
1915 .await
1916 .unwrap();
1917
1918 let fake_room_id = room_id!("!roomid:localhost");
1919
1920 let encryption_settings = identity_based_strategy_settings();
1922
1923 let request_result = machine
1924 .share_room_key(
1925 fake_room_id,
1926 vec![user1, user2].into_iter(),
1927 encryption_settings.clone(),
1928 )
1929 .await;
1930
1931 assert_let!(
1934 Err(OlmError::SessionRecipientCollectionError(
1935 SessionRecipientCollectionError::VerifiedUserChangedIdentity(affected_users)
1936 )) = request_result
1937 );
1938 assert_eq!(2, affected_users.len());
1940
1941 machine
1943 .get_identity(user1, None)
1944 .await
1945 .unwrap()
1946 .unwrap()
1947 .withdraw_verification()
1948 .await
1949 .unwrap();
1950
1951 let verification_request = machine
1953 .get_identity(user2, None)
1954 .await
1955 .unwrap()
1956 .unwrap()
1957 .other()
1958 .unwrap()
1959 .verify()
1960 .await
1961 .unwrap();
1962
1963 let master_key =
1964 &machine.get_identity(user2, None).await.unwrap().unwrap().other().unwrap().master_key;
1965
1966 let my_identity = machine
1967 .get_identity(KeyDistributionTestData::me_id(), None)
1968 .await
1969 .expect("Should not fail to find own identity")
1970 .expect("Our own identity should not be missing")
1971 .own()
1972 .expect("Our own identity should be of type Own");
1973
1974 let msk = json!({ user2: serde_json::to_value(master_key).expect("Should not fail to serialize")});
1975 let ssk =
1976 serde_json::to_value(&MaloIdentityChangeDataSet::updated_key_query().self_signing_keys)
1977 .expect("Should not fail to serialize");
1978
1979 let kq_response = simulate_key_query_response_for_verification(
1980 verification_request,
1981 my_identity,
1982 KeyDistributionTestData::me_id(),
1983 user2,
1984 msk,
1985 ssk,
1986 );
1987
1988 machine
1989 .mark_request_as_sent(
1990 &TransactionId::new(),
1991 crate::types::requests::AnyIncomingResponse::KeysQuery(&kq_response),
1992 )
1993 .await
1994 .unwrap();
1995
1996 assert!(machine.get_identity(user2, None).await.unwrap().unwrap().is_verified());
1997
1998 machine
2000 .share_room_key(
2001 fake_room_id,
2002 vec![user1, user2].into_iter(),
2003 encryption_settings.clone(),
2004 )
2005 .await
2006 .unwrap();
2007 }
2008
2009 #[async_test]
2010 async fn test_should_rotate_based_on_visibility() {
2011 let machine = test_machine().await;
2012 import_known_users_to_test_machine(&machine).await;
2013
2014 let strategy = CollectStrategy::AllDevices;
2015
2016 let encryption_settings = EncryptionSettings {
2017 sharing_strategy: strategy.clone(),
2018 history_visibility: HistoryVisibility::Invited,
2019 ..Default::default()
2020 };
2021
2022 let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2023
2024 let _ = collect_session_recipients(
2025 machine.store(),
2026 vec![KeyDistributionTestData::dan_id()].into_iter(),
2027 &encryption_settings,
2028 &group_session,
2029 )
2030 .await
2031 .unwrap();
2032
2033 let encryption_settings = EncryptionSettings {
2035 sharing_strategy: strategy.clone(),
2036 history_visibility: HistoryVisibility::Shared,
2037 ..Default::default()
2038 };
2039
2040 let share_result = collect_session_recipients(
2041 machine.store(),
2042 vec![KeyDistributionTestData::dan_id()].into_iter(),
2043 &encryption_settings,
2044 &group_session,
2045 )
2046 .await
2047 .unwrap();
2048
2049 assert!(share_result.should_rotate);
2050 }
2051
2052 #[async_test]
2056 async fn test_should_rotate_based_on_device_excluded() {
2057 let machine = test_machine().await;
2058 import_known_users_to_test_machine(&machine).await;
2059
2060 let fake_room_id = room_id!("!roomid:localhost");
2061 let encryption_settings = all_devices_strategy_settings();
2062
2063 let requests = machine
2064 .share_room_key(
2065 fake_room_id,
2066 vec![KeyDistributionTestData::dan_id()].into_iter(),
2067 encryption_settings.clone(),
2068 )
2069 .await
2070 .unwrap();
2071
2072 for r in requests {
2073 machine
2074 .inner
2075 .group_session_manager
2076 .mark_request_as_sent(r.as_ref().txn_id.as_ref())
2077 .await
2078 .unwrap();
2079 }
2080 let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
2082 let txn_id = TransactionId::new();
2083 machine.mark_request_as_sent(&txn_id, &keys_query).await.unwrap();
2084
2085 let group_session =
2086 machine.store().get_outbound_group_session(fake_room_id).await.unwrap().unwrap();
2087 let share_result = collect_session_recipients(
2089 machine.store(),
2090 vec![KeyDistributionTestData::dan_id()].into_iter(),
2091 &encryption_settings,
2092 &group_session,
2093 )
2094 .await
2095 .unwrap();
2096
2097 assert!(share_result.should_rotate);
2098 }
2099
2100 async fn unsigned_of_verified_setup() -> OlmMachine {
2108 use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
2109
2110 let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
2111
2112 let own_keys = DataSet::own_keys_query_response_1();
2114 machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
2115
2116 machine
2118 .import_cross_signing_keys(CrossSigningKeyExport {
2119 master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
2120 self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2121 user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
2122 })
2123 .await
2124 .unwrap();
2125
2126 let bob_keys = DataSet::bob_keys_query_response_signed();
2128 machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
2129
2130 let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
2133 assert!(bob_identity.other().unwrap().is_verified());
2134
2135 let bob_signed_device = machine
2136 .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
2137 .await
2138 .unwrap()
2139 .unwrap();
2140 assert!(bob_signed_device.is_verified());
2141 assert!(bob_signed_device.device_owner_identity.is_some());
2142
2143 let bob_unsigned_device = machine
2144 .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
2145 .await
2146 .unwrap()
2147 .unwrap();
2148 assert!(!bob_unsigned_device.is_verified());
2149
2150 machine
2151 }
2152
2153 fn all_devices_strategy_settings() -> EncryptionSettings {
2155 EncryptionSettings { sharing_strategy: CollectStrategy::AllDevices, ..Default::default() }
2156 }
2157
2158 fn error_on_verification_problem_encryption_settings() -> EncryptionSettings {
2161 EncryptionSettings {
2162 sharing_strategy: CollectStrategy::ErrorOnVerifiedUserProblem,
2163 ..Default::default()
2164 }
2165 }
2166
2167 fn identity_based_strategy_settings() -> EncryptionSettings {
2169 EncryptionSettings {
2170 sharing_strategy: CollectStrategy::IdentityBasedStrategy,
2171 ..Default::default()
2172 }
2173 }
2174
2175 fn create_test_outbound_group_session(
2178 machine: &OlmMachine,
2179 encryption_settings: &EncryptionSettings,
2180 ) -> OutboundGroupSession {
2181 OutboundGroupSession::new(
2182 machine.device_id().into(),
2183 Arc::new(machine.identity_keys()),
2184 room_id!("!roomid:localhost"),
2185 encryption_settings.clone(),
2186 )
2187 .expect("creating an outbound group session should not fail")
2188 }
2189}