matrix_sdk_crypto/session_manager/group_sessions/
share_strategy.rs

1// Copyright 2024 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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;
27#[cfg(doc)]
28use crate::{Device, UserIdentity};
29use crate::{
30    DeviceData, EncryptionSettings, LocalTrust, OlmError, OwnUserIdentityData, UserIdentityData,
31    error::{OlmResult, SessionRecipientCollectionError},
32    olm::ShareInfo,
33    store::Store,
34};
35
36/// Strategy to collect the devices that should receive room keys for the
37/// current discussion.
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
39#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
40#[serde(from = "CollectStrategyDeserializationHelper")]
41pub enum CollectStrategy {
42    /// Share with all (unblacklisted) devices.
43    #[default]
44    AllDevices,
45
46    /// Share with all devices, except errors for *verified* users cause sharing
47    /// to fail with an error.
48    ///
49    /// In this strategy, if a verified user has an unsigned device,
50    /// key sharing will fail with a
51    /// [`SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice`].
52    /// If a verified user has replaced their identity, key
53    /// sharing will fail with a
54    /// [`SessionRecipientCollectionError::VerifiedUserChangedIdentity`].
55    ///
56    /// Otherwise, keys are shared with unsigned devices as normal.
57    ///
58    /// Once the problematic devices are blacklisted or whitelisted the
59    /// caller can retry to share a second time.
60    ErrorOnVerifiedUserProblem,
61
62    /// Share based on identity. Only distribute to devices signed by their
63    /// owner. If a user has no published identity he will not receive
64    /// any room keys.
65    IdentityBasedStrategy,
66
67    /// Only share keys with devices that we "trust". A device is trusted if any
68    /// of the following is true:
69    ///     - It was manually marked as trusted.
70    ///     - It was marked as verified via interactive verification.
71    ///     - It is signed by its owner identity, and this identity has been
72    ///       trusted via interactive verification.
73    ///     - It is the current own device of the user.
74    OnlyTrustedDevices,
75}
76
77impl CollectStrategy {
78    /// Creates an identity based strategy
79    pub const fn new_identity_based() -> Self {
80        CollectStrategy::IdentityBasedStrategy
81    }
82}
83
84/// Deserialization helper for [`CollectStrategy`].
85#[derive(Deserialize)]
86enum CollectStrategyDeserializationHelper {
87    /// `AllDevices`, `ErrorOnVerifiedUserProblem` and `OnlyTrustedDevices` used
88    /// to be implemented as a single strategy with flags.
89    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/// Returned by `collect_session_recipients`.
130///
131/// Information indicating whether the session needs to be rotated
132/// (`should_rotate`) and the list of users/devices that should receive
133/// (`devices`) or not the session,  including withheld reason
134/// `withheld_devices`.
135#[derive(Debug, Default)]
136pub(crate) struct CollectRecipientsResult {
137    /// If true the outbound group session should be rotated
138    pub should_rotate: bool,
139    /// The map of user|device that should receive the session
140    pub devices: BTreeMap<OwnedUserId, Vec<DeviceData>>,
141    /// The map of user|device that won't receive the key with the withheld
142    /// code.
143    pub withheld_devices: Vec<(DeviceData, WithheldCode)>,
144}
145
146/// Given a list of user and an outbound session, return the list of users
147/// and their devices that this session should be shared with.
148///
149/// Returns information indicating whether the session needs to be rotated
150/// and the list of users/devices that should receive or not the session
151/// (with withheld reason).
152#[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    // To protect the room history we need to rotate the session if either:
168    //
169    // 1. Any user left the room.
170    // 2. Any of the users' devices got deleted or blacklisted.
171    // 3. The history visibility changed.
172    // 4. The encryption algorithm changed.
173    //
174    // `result.should_rotate` is true if the first or second in that list is true;
175    // we now need to check for the other two.
176    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
193/// Given a list of users and a [`CollectStrategy`], return the list of devices
194/// that cryptographic keys should be shared with, or that withheld notices
195/// should be sent to.
196///
197/// If an existing [`OutboundGroupSession`] is provided, will also check the
198/// list of devices that the session has been *previously* shared with, and
199/// if that list is too broad, returns a flag indicating that the session should
200/// be rotated (e.g., because a device has been deleted or a user has left the
201/// chat).
202pub(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 we have an outbound session, check if a user is missing from the set of
215    // users that should get the session but is in the set of users that
216    // received the session.
217    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    // Get the recipient and withheld devices, based on the collection strategy.
230    match share_strategy {
231        CollectStrategy::AllDevices => {
232            for user_id in users {
233                trace!(?user_id, "CollectStrategy::AllDevices: Considering recipient devices",);
234                let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
235                let device_owner_identity = store.get_user_identity(user_id).await?;
236
237                let recipient_devices = split_devices_for_user_for_all_devices_strategy(
238                    user_devices,
239                    &own_identity,
240                    &device_owner_identity,
241                );
242                update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
243            }
244        }
245        CollectStrategy::ErrorOnVerifiedUserProblem => {
246            let mut unsigned_devices_of_verified_users: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>> =
247                Default::default();
248
249            for user_id in users {
250                trace!(
251                    ?user_id,
252                    "CollectStrategy::ErrorOnVerifiedUserProblem: Considering recipient devices"
253                );
254                let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
255
256                let device_owner_identity = store.get_user_identity(user_id).await?;
257
258                if has_identity_verification_violation(
259                    own_identity.as_ref(),
260                    device_owner_identity.as_ref(),
261                ) {
262                    verified_users_with_new_identities.push(user_id.to_owned());
263                    // No point considering the individual devices of this user.
264                    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 `error_on_verified_user_problem` is set, then
290            // `unsigned_devices_of_verified_users` may be populated. If so, we need to bail
291            // out with an error.
292            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            // We require our own cross-signing to be properly set up for the
302            // identity-based strategy, so return an error if it isn't.
303            match &own_identity {
304                None => {
305                    return Err(OlmError::SessionRecipientCollectionError(
306                        SessionRecipientCollectionError::CrossSigningNotSetup,
307                    ));
308                }
309                Some(identity) if !identity.is_verified() => {
310                    return Err(OlmError::SessionRecipientCollectionError(
311                        SessionRecipientCollectionError::SendingFromUnverifiedDevice,
312                    ));
313                }
314                Some(_) => (),
315            }
316
317            for user_id in users {
318                trace!(
319                    ?user_id,
320                    "CollectStrategy::IdentityBasedStrategy: Considering recipient devices"
321                );
322                let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
323
324                let device_owner_identity = store.get_user_identity(user_id).await?;
325
326                if has_identity_verification_violation(
327                    own_identity.as_ref(),
328                    device_owner_identity.as_ref(),
329                ) {
330                    verified_users_with_new_identities.push(user_id.to_owned());
331                    // No point considering the individual devices of this user.
332                    continue;
333                }
334
335                let recipient_devices = split_devices_for_user_for_identity_based_strategy(
336                    user_devices,
337                    &device_owner_identity,
338                );
339
340                update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
341            }
342        }
343
344        CollectStrategy::OnlyTrustedDevices => {
345            for user_id in users {
346                trace!(
347                    ?user_id,
348                    "CollectStrategy::OnlyTrustedDevices: Considering recipient devices"
349                );
350                let user_devices = store.get_device_data_for_user_filtered(user_id).await?;
351                let device_owner_identity = store.get_user_identity(user_id).await?;
352
353                let recipient_devices = split_devices_for_user_for_only_trusted_devices(
354                    user_devices,
355                    &own_identity,
356                    &device_owner_identity,
357                );
358
359                update_recipients_for_user(&mut result, outbound, user_id, recipient_devices);
360            }
361        }
362    }
363
364    // We may have encountered previously-verified users who have changed their
365    // identities. If so, we bail out with an error.
366    if !verified_users_with_new_identities.is_empty() {
367        return Err(OlmError::SessionRecipientCollectionError(
368            SessionRecipientCollectionError::VerifiedUserChangedIdentity(
369                verified_users_with_new_identities,
370            ),
371        ));
372    }
373
374    trace!(result.should_rotate, "Done calculating group session recipients");
375
376    Ok(result)
377}
378
379/// Update this [`CollectRecipientsResult`] with the device list for a specific
380/// user.
381fn update_recipients_for_user(
382    recipients: &mut CollectRecipientsResult,
383    outbound: Option<&OutboundGroupSession>,
384    user_id: &UserId,
385    recipient_devices: RecipientDevicesForUser,
386) {
387    // If we haven't already concluded that the session should be
388    // rotated for other reasons, we also need to check whether any
389    // of the devices in the session got deleted or blacklisted in the
390    // meantime. If so, we should also rotate the session.
391    if let Some(outbound) = outbound
392        && !recipients.should_rotate
393    {
394        recipients.should_rotate =
395            is_session_overshared_for_user(outbound, user_id, &recipient_devices.allowed_devices)
396    }
397
398    recipients
399        .devices
400        .entry(user_id.to_owned())
401        .or_default()
402        .extend(recipient_devices.allowed_devices);
403    recipients.withheld_devices.extend(recipient_devices.denied_devices_with_code);
404}
405
406/// Check if the session has been shared with a device belonging to the given
407/// user, that is no longer in the pool of devices that should participate in
408/// the discussion.
409///
410/// # Arguments
411///
412/// * `outbound_session` - the outbound group session to check for oversharing.
413/// * `user_id` - the ID of the user we are checking the devices for.
414/// * `recipient_devices` - the list of devices belonging to `user_id` that we
415///   expect to share the session with.
416///
417/// # Returns
418///
419/// `true` if the session has been shared with any devices belonging to
420/// `user_id` that are not in `recipient_devices`. Otherwise, `false`.
421fn is_session_overshared_for_user(
422    outbound_session: &OutboundGroupSession,
423    user_id: &UserId,
424    recipient_devices: &[DeviceData],
425) -> bool {
426    // Device IDs that should receive this session
427    let recipient_device_ids: BTreeSet<&DeviceId> =
428        recipient_devices.iter().map(|d| d.device_id()).collect();
429
430    let view = outbound_session.sharing_view();
431    let newly_deleted_or_blacklisted: BTreeSet<&DeviceId> = view
432        .iter_shares(Some(user_id), None)
433        .filter_map(|(_user_id, device_id, info)| {
434            // If a devices who we've shared the session with before is not in the
435            // list of devices that should receive the session, we need to rotate.
436            // We also collect all of those device IDs to log them out.
437            if matches!(info, ShareInfo::Shared(_)) && !recipient_device_ids.contains(device_id) {
438                Some(device_id)
439            } else {
440                None
441            }
442        })
443        .collect();
444
445    let should_rotate = !newly_deleted_or_blacklisted.is_empty();
446    if should_rotate {
447        debug!(
448            "Rotating a room key due to these devices being deleted/blacklisted {:?}",
449            newly_deleted_or_blacklisted,
450        );
451    }
452    should_rotate
453}
454
455#[cfg(feature = "experimental-send-custom-to-device")]
456/// Partition the devices based on the given collect strategy
457pub(crate) async fn split_devices_for_share_strategy(
458    store: &Store,
459    devices: Vec<DeviceData>,
460    share_strategy: CollectStrategy,
461) -> OlmResult<(Vec<DeviceData>, Vec<(DeviceData, WithheldCode)>)> {
462    let own_identity = store.get_user_identity(store.user_id()).await?.and_then(|i| i.into_own());
463
464    let mut verified_users_with_new_identities: BTreeSet<OwnedUserId> = Default::default();
465
466    let mut allowed_devices: Vec<DeviceData> = Default::default();
467    let mut blocked_devices: Vec<(DeviceData, WithheldCode)> = Default::default();
468
469    let mut user_identities_cache: BTreeMap<OwnedUserId, Option<UserIdentityData>> =
470        Default::default();
471    let mut get_user_identity = async move |user_id| -> OlmResult<_> {
472        match user_identities_cache.get(user_id) {
473            Some(user_identity) => Ok(user_identity.clone()),
474            None => {
475                let user_identity = store.get_user_identity(user_id).await?;
476                user_identities_cache.insert(user_id.to_owned(), user_identity.clone());
477                Ok(user_identity)
478            }
479        }
480    };
481
482    match share_strategy {
483        CollectStrategy::AllDevices => {
484            for device in devices.iter() {
485                let user_id = device.user_id();
486                let device_owner_identity = get_user_identity(user_id).await?;
487
488                if let Some(withheld_code) = withheld_code_for_device_for_all_devices_strategy(
489                    device,
490                    &own_identity,
491                    &device_owner_identity,
492                ) {
493                    blocked_devices.push((device.clone(), withheld_code));
494                } else {
495                    allowed_devices.push(device.clone());
496                }
497            }
498        }
499
500        CollectStrategy::ErrorOnVerifiedUserProblem => {
501            // We throw an error if any user has a verification violation.  So
502            // we loop through all the devices given, and check if the
503            // associated user has a verification violation.  If so, we add the
504            // device to `unsigned_devices_of_verified_users`, which will be
505            // returned with the error.
506            let mut unsigned_devices_of_verified_users: BTreeMap<OwnedUserId, Vec<OwnedDeviceId>> =
507                Default::default();
508            let mut add_device_to_unsigned_devices_map = |user_id: &UserId, device: &DeviceData| {
509                let device_id = device.device_id().to_owned();
510                if let Some(devices) = unsigned_devices_of_verified_users.get_mut(user_id) {
511                    devices.push(device_id);
512                } else {
513                    unsigned_devices_of_verified_users.insert(user_id.to_owned(), vec![device_id]);
514                }
515            };
516
517            for device in devices.iter() {
518                let user_id = device.user_id();
519                let device_owner_identity = get_user_identity(user_id).await?;
520
521                if has_identity_verification_violation(
522                    own_identity.as_ref(),
523                    device_owner_identity.as_ref(),
524                ) {
525                    verified_users_with_new_identities.insert(user_id.to_owned());
526                } else {
527                    match handle_device_for_user_for_error_on_verified_user_problem_strategy(
528                        device,
529                        own_identity.as_ref(),
530                        device_owner_identity.as_ref(),
531                    ) {
532                        ErrorOnVerifiedUserProblemDeviceDecision::Ok => {
533                            allowed_devices.push(device.clone())
534                        }
535                        ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => {
536                            blocked_devices.push((device.clone(), code))
537                        }
538                        ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
539                            add_device_to_unsigned_devices_map(user_id, device);
540                        }
541                    }
542                }
543            }
544
545            if !unsigned_devices_of_verified_users.is_empty() {
546                return Err(OlmError::SessionRecipientCollectionError(
547                    SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
548                        unsigned_devices_of_verified_users,
549                    ),
550                ));
551            }
552        }
553
554        CollectStrategy::IdentityBasedStrategy => {
555            // We require our own cross-signing to be properly set up for the
556            // identity-based strategy, so return an error if it isn't.
557            match &own_identity {
558                None => {
559                    return Err(OlmError::SessionRecipientCollectionError(
560                        SessionRecipientCollectionError::CrossSigningNotSetup,
561                    ));
562                }
563                Some(identity) if !identity.is_verified() => {
564                    return Err(OlmError::SessionRecipientCollectionError(
565                        SessionRecipientCollectionError::SendingFromUnverifiedDevice,
566                    ));
567                }
568                Some(_) => (),
569            }
570
571            for device in devices.iter() {
572                let user_id = device.user_id();
573                let device_owner_identity = get_user_identity(user_id).await?;
574
575                if has_identity_verification_violation(
576                    own_identity.as_ref(),
577                    device_owner_identity.as_ref(),
578                ) {
579                    verified_users_with_new_identities.insert(user_id.to_owned());
580                } else if let Some(device_owner_identity) = device_owner_identity {
581                    if let Some(withheld_code) =
582                        withheld_code_for_device_with_owner_for_identity_based_strategy(
583                            device,
584                            &device_owner_identity,
585                        )
586                    {
587                        blocked_devices.push((device.clone(), withheld_code));
588                    } else {
589                        allowed_devices.push(device.clone());
590                    }
591                } else {
592                    panic!("Should have verification violation if device_owner_identity is None")
593                }
594            }
595        }
596
597        CollectStrategy::OnlyTrustedDevices => {
598            for device in devices.iter() {
599                let user_id = device.user_id();
600                let device_owner_identity = get_user_identity(user_id).await?;
601
602                if let Some(withheld_code) =
603                    withheld_code_for_device_for_only_trusted_devices_strategy(
604                        device,
605                        &own_identity,
606                        &device_owner_identity,
607                    )
608                {
609                    blocked_devices.push((device.clone(), withheld_code));
610                } else {
611                    allowed_devices.push(device.clone());
612                }
613            }
614        }
615    }
616
617    if !verified_users_with_new_identities.is_empty() {
618        return Err(OlmError::SessionRecipientCollectionError(
619            SessionRecipientCollectionError::VerifiedUserChangedIdentity(
620                verified_users_with_new_identities.into_iter().collect(),
621            ),
622        ));
623    }
624
625    Ok((allowed_devices, blocked_devices))
626}
627
628pub(crate) async fn withheld_code_for_device_for_share_strategy(
629    device: &DeviceData,
630    share_strategy: CollectStrategy,
631    own_identity: &Option<OwnUserIdentityData>,
632    device_owner_identity: &Option<UserIdentityData>,
633) -> OlmResult<Option<WithheldCode>> {
634    match share_strategy {
635        CollectStrategy::AllDevices => Ok(withheld_code_for_device_for_all_devices_strategy(
636            device,
637            own_identity,
638            device_owner_identity,
639        )),
640        CollectStrategy::ErrorOnVerifiedUserProblem => {
641            if has_identity_verification_violation(
642                own_identity.as_ref(),
643                device_owner_identity.as_ref(),
644            ) {
645                return Err(OlmError::SessionRecipientCollectionError(
646                    SessionRecipientCollectionError::VerifiedUserChangedIdentity(vec![
647                        device.user_id().to_owned(),
648                    ]),
649                ));
650            }
651            match handle_device_for_user_for_error_on_verified_user_problem_strategy(
652                device,
653                own_identity.as_ref(),
654                device_owner_identity.as_ref(),
655            ) {
656                ErrorOnVerifiedUserProblemDeviceDecision::Ok => Ok(None),
657                ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => Ok(Some(code)),
658                ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
659                    Err(OlmError::SessionRecipientCollectionError(
660                        SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
661                            BTreeMap::from([(
662                                device.user_id().to_owned(),
663                                vec![device.device_id().to_owned()],
664                            )]),
665                        ),
666                    ))
667                }
668            }
669        }
670        CollectStrategy::IdentityBasedStrategy => {
671            // We require our own cross-signing to be properly set up for the
672            // identity-based strategy, so return false if it isn't.
673            match &own_identity {
674                None => {
675                    return Err(OlmError::SessionRecipientCollectionError(
676                        SessionRecipientCollectionError::CrossSigningNotSetup,
677                    ));
678                }
679                Some(identity) if !identity.is_verified() => {
680                    return Err(OlmError::SessionRecipientCollectionError(
681                        SessionRecipientCollectionError::SendingFromUnverifiedDevice,
682                    ));
683                }
684                Some(_) => (),
685            }
686
687            if has_identity_verification_violation(
688                own_identity.as_ref(),
689                device_owner_identity.as_ref(),
690            ) {
691                Err(OlmError::SessionRecipientCollectionError(
692                    SessionRecipientCollectionError::VerifiedUserChangedIdentity(vec![
693                        device.user_id().to_owned(),
694                    ]),
695                ))
696            } else if let Some(device_owner_identity) = device_owner_identity {
697                Ok(withheld_code_for_device_with_owner_for_identity_based_strategy(
698                    device,
699                    device_owner_identity,
700                ))
701            } else {
702                panic!("Should have verification violation if device_owner_identity is None")
703            }
704        }
705        CollectStrategy::OnlyTrustedDevices => {
706            Ok(withheld_code_for_device_for_only_trusted_devices_strategy(
707                device,
708                own_identity,
709                device_owner_identity,
710            ))
711        }
712    }
713}
714
715/// Result type for [`split_devices_for_user_for_all_devices_strategy`],
716/// [`split_devices_for_user_for_error_on_verified_user_problem_strategy`],
717/// [`split_devices_for_user_for_identity_based_strategy`],
718/// [`split_devices_for_user_for_only_trusted_devices`].
719///
720/// A partitioning of the devices for a given user.
721#[derive(Default)]
722struct RecipientDevicesForUser {
723    /// Devices that should receive the room key.
724    allowed_devices: Vec<DeviceData>,
725    /// Devices that should receive a withheld code.
726    denied_devices_with_code: Vec<(DeviceData, WithheldCode)>,
727}
728
729/// Result type for
730/// [`split_devices_for_user_for_error_on_verified_user_problem_strategy`].
731enum ErrorOnVerifiedUserProblemResult {
732    /// We found devices that should cause the transmission to fail, due to
733    /// being an unsigned device belonging to a verified user. Only
734    /// populated when `error_on_verified_user_problem` is set.
735    UnsignedDevicesOfVerifiedUser(Vec<OwnedDeviceId>),
736
737    /// There were no unsigned devices of verified users.
738    Devices(RecipientDevicesForUser),
739}
740
741/// Partition the list of a user's devices according to whether they should
742/// receive the key, for [`CollectStrategy::AllDevices`].
743fn split_devices_for_user_for_all_devices_strategy(
744    user_devices: HashMap<OwnedDeviceId, DeviceData>,
745    own_identity: &Option<OwnUserIdentityData>,
746    device_owner_identity: &Option<UserIdentityData>,
747) -> RecipientDevicesForUser {
748    let (left, right) = user_devices.into_values().partition_map(|d| {
749        if let Some(withheld_code) = withheld_code_for_device_for_all_devices_strategy(
750            &d,
751            own_identity,
752            device_owner_identity,
753        ) {
754            Either::Right((d, withheld_code))
755        } else {
756            Either::Left(d)
757        }
758    });
759
760    RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
761}
762
763/// Determine whether we should withhold encrypted messages from the given
764/// device, for [`CollectStrategy::AllDevices`], and if so, what withheld code
765/// to send.
766fn withheld_code_for_device_for_all_devices_strategy(
767    device_data: &DeviceData,
768    own_identity: &Option<OwnUserIdentityData>,
769    device_owner_identity: &Option<UserIdentityData>,
770) -> Option<WithheldCode> {
771    if device_data.is_blacklisted() {
772        Some(WithheldCode::Blacklisted)
773    } else if device_data.is_dehydrated()
774        && should_withhold_to_dehydrated_device(
775            device_data,
776            own_identity.as_ref(),
777            device_owner_identity.as_ref(),
778        )
779    {
780        Some(WithheldCode::Unverified)
781    } else {
782        None
783    }
784}
785
786/// Helper for [`split_devices_for_user_for_all_devices_strategy`].
787///
788/// Given a dehydrated device `device`, decide if we should withhold the room
789/// key from it.
790///
791/// Dehydrated devices must be signed by their owners (whether or not we have
792/// verified the owner), and, if we previously verified the owner, they must be
793/// verified still (i.e., they must not have a verification violation).
794fn should_withhold_to_dehydrated_device(
795    device: &DeviceData,
796    own_identity: Option<&OwnUserIdentityData>,
797    device_owner_identity: Option<&UserIdentityData>,
798) -> bool {
799    device_owner_identity.is_none_or(|owner_id| {
800        // Dehydrated devices must be signed by their owners
801        !device.is_cross_signed_by_owner(owner_id) ||
802
803        // If the user has changed identity since we verified them, withhold the message
804        (owner_id.was_previously_verified() && !is_user_verified(own_identity, owner_id))
805    })
806}
807
808/// Partition the list of a user's devices according to whether they should
809/// receive the key, for [`CollectStrategy::ErrorOnVerifiedUserProblem`].
810///
811/// This function returns one of two values:
812///
813/// * A list of the devices that should cause the transmission to fail due to
814///   being unsigned. In this case, we don't bother to return the rest of the
815///   devices, because we assume transmission will fail.
816///
817/// * Otherwise, returns a [`RecipientDevicesForUser`] which lists, separately,
818///   the devices that should receive the room key, and those that should
819///   receive a withheld code.
820fn split_devices_for_user_for_error_on_verified_user_problem_strategy(
821    user_devices: HashMap<OwnedDeviceId, DeviceData>,
822    own_identity: &Option<OwnUserIdentityData>,
823    device_owner_identity: &Option<UserIdentityData>,
824) -> ErrorOnVerifiedUserProblemResult {
825    let mut recipient_devices = RecipientDevicesForUser::default();
826
827    // We construct unsigned_devices_of_verified_users lazily, because chances are
828    // we won't need it.
829    let mut unsigned_devices_of_verified_users: Option<Vec<OwnedDeviceId>> = None;
830
831    for d in user_devices.into_values() {
832        match handle_device_for_user_for_error_on_verified_user_problem_strategy(
833            &d,
834            own_identity.as_ref(),
835            device_owner_identity.as_ref(),
836        ) {
837            ErrorOnVerifiedUserProblemDeviceDecision::Ok => {
838                recipient_devices.allowed_devices.push(d)
839            }
840            ErrorOnVerifiedUserProblemDeviceDecision::Withhold(code) => {
841                recipient_devices.denied_devices_with_code.push((d, code))
842            }
843            ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified => {
844                unsigned_devices_of_verified_users
845                    .get_or_insert_with(Vec::default)
846                    .push(d.device_id().to_owned())
847            }
848        }
849    }
850
851    if let Some(devices) = unsigned_devices_of_verified_users {
852        ErrorOnVerifiedUserProblemResult::UnsignedDevicesOfVerifiedUser(devices)
853    } else {
854        ErrorOnVerifiedUserProblemResult::Devices(recipient_devices)
855    }
856}
857
858/// Result type for
859/// [`handle_device_for_user_for_error_on_verified_user_problem_strategy`].
860enum ErrorOnVerifiedUserProblemDeviceDecision {
861    Ok,
862    Withhold(WithheldCode),
863    UnsignedOfVerified,
864}
865
866fn handle_device_for_user_for_error_on_verified_user_problem_strategy(
867    device: &DeviceData,
868    own_identity: Option<&OwnUserIdentityData>,
869    device_owner_identity: Option<&UserIdentityData>,
870) -> ErrorOnVerifiedUserProblemDeviceDecision {
871    if device.is_blacklisted() {
872        ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Blacklisted)
873    } else if device.local_trust_state() == LocalTrust::Ignored {
874        // Ignore the trust state of that device and share
875        ErrorOnVerifiedUserProblemDeviceDecision::Ok
876    } else if is_unsigned_device_of_verified_user(own_identity, device_owner_identity, device) {
877        ErrorOnVerifiedUserProblemDeviceDecision::UnsignedOfVerified
878    } else if device.is_dehydrated()
879        && device_owner_identity.is_none_or(|owner_id| {
880            // Dehydrated devices must be signed by their owners, whether or not that
881            // owner is verified
882            !device.is_cross_signed_by_owner(owner_id)
883        })
884    {
885        ErrorOnVerifiedUserProblemDeviceDecision::Withhold(WithheldCode::Unverified)
886    } else {
887        ErrorOnVerifiedUserProblemDeviceDecision::Ok
888    }
889}
890
891fn split_devices_for_user_for_identity_based_strategy(
892    user_devices: HashMap<OwnedDeviceId, DeviceData>,
893    device_owner_identity: &Option<UserIdentityData>,
894) -> RecipientDevicesForUser {
895    match device_owner_identity {
896        None => {
897            // withheld all the users devices, we need to have an identity for this
898            // distribution mode
899            RecipientDevicesForUser {
900                allowed_devices: Vec::default(),
901                denied_devices_with_code: user_devices
902                    .into_values()
903                    .map(|d| (d, WithheldCode::Unverified))
904                    .collect(),
905            }
906        }
907        Some(device_owner_identity) => {
908            // Only accept devices signed by the current identity
909            let (recipients, withheld_recipients): (
910                Vec<DeviceData>,
911                Vec<(DeviceData, WithheldCode)>,
912            ) = user_devices.into_values().partition_map(|d| {
913                if let Some(withheld_code) =
914                    withheld_code_for_device_with_owner_for_identity_based_strategy(
915                        &d,
916                        device_owner_identity,
917                    )
918                {
919                    Either::Right((d, withheld_code))
920                } else {
921                    Either::Left(d)
922                }
923            });
924            RecipientDevicesForUser {
925                allowed_devices: recipients,
926                denied_devices_with_code: withheld_recipients,
927            }
928        }
929    }
930}
931
932/// Determine whether we should withhold encrypted messages from the given
933/// device, for [`CollectStrategy::IdentityBased`], and if so, what withheld
934/// code to send.
935fn withheld_code_for_device_with_owner_for_identity_based_strategy(
936    device_data: &DeviceData,
937    device_owner_identity: &UserIdentityData,
938) -> Option<WithheldCode> {
939    if device_data.is_cross_signed_by_owner(device_owner_identity) {
940        None
941    } else {
942        Some(WithheldCode::Unverified)
943    }
944}
945
946/// Partition the list of a user's devices according to whether they should
947/// receive the key, for [`CollectStrategy::OnlyTrustedDevices`].
948fn split_devices_for_user_for_only_trusted_devices(
949    user_devices: HashMap<OwnedDeviceId, DeviceData>,
950    own_identity: &Option<OwnUserIdentityData>,
951    device_owner_identity: &Option<UserIdentityData>,
952) -> RecipientDevicesForUser {
953    let (left, right) = user_devices.into_values().partition_map(|d| {
954        if let Some(withheld_code) = withheld_code_for_device_for_only_trusted_devices_strategy(
955            &d,
956            own_identity,
957            device_owner_identity,
958        ) {
959            Either::Right((d, withheld_code))
960        } else {
961            Either::Left(d)
962        }
963    });
964    RecipientDevicesForUser { allowed_devices: left, denied_devices_with_code: right }
965}
966
967/// Determine whether we should withhold encrypted messages from the given
968/// device, for [`CollectStrategy::OnlyTrustedDevices`], and if so, what
969/// withheld code to send.
970fn withheld_code_for_device_for_only_trusted_devices_strategy(
971    device_data: &DeviceData,
972    own_identity: &Option<OwnUserIdentityData>,
973    device_owner_identity: &Option<UserIdentityData>,
974) -> Option<WithheldCode> {
975    match (
976        device_data.local_trust_state(),
977        device_data.is_cross_signing_trusted(own_identity, device_owner_identity),
978    ) {
979        (LocalTrust::BlackListed, _) => Some(WithheldCode::Blacklisted),
980        (LocalTrust::Ignored | LocalTrust::Verified, _) => None,
981        (LocalTrust::Unset, false) => Some(WithheldCode::Unverified),
982        (LocalTrust::Unset, true) => None,
983    }
984}
985
986fn is_unsigned_device_of_verified_user(
987    own_identity: Option<&OwnUserIdentityData>,
988    device_owner_identity: Option<&UserIdentityData>,
989    device_data: &DeviceData,
990) -> bool {
991    device_owner_identity.is_some_and(|device_owner_identity| {
992        is_user_verified(own_identity, device_owner_identity)
993            && !device_data.is_cross_signed_by_owner(device_owner_identity)
994    })
995}
996
997/// Check if the user was previously verified, but they have now changed their
998/// identity so that they are no longer verified.
999///
1000/// This is much the same as [`UserIdentity::has_verification_violation`], but
1001/// works with a low-level [`UserIdentityData`] rather than higher-level
1002/// [`UserIdentity`].
1003fn has_identity_verification_violation(
1004    own_identity: Option<&OwnUserIdentityData>,
1005    device_owner_identity: Option<&UserIdentityData>,
1006) -> bool {
1007    device_owner_identity.is_some_and(|device_owner_identity| {
1008        device_owner_identity.was_previously_verified()
1009            && !is_user_verified(own_identity, device_owner_identity)
1010    })
1011}
1012
1013fn is_user_verified(
1014    own_identity: Option<&OwnUserIdentityData>,
1015    user_identity: &UserIdentityData,
1016) -> bool {
1017    match user_identity {
1018        UserIdentityData::Own(own_identity) => own_identity.is_verified(),
1019        UserIdentityData::Other(other_identity) => {
1020            own_identity.is_some_and(|oi| oi.is_identity_verified(other_identity))
1021        }
1022    }
1023}
1024
1025#[cfg(test)]
1026mod tests {
1027    use std::{collections::BTreeMap, iter, ops::Deref, sync::Arc};
1028
1029    use assert_matches::assert_matches;
1030    use assert_matches2::assert_let;
1031    use insta::{assert_snapshot, with_settings};
1032    use matrix_sdk_common::deserialized_responses::WithheldCode;
1033    use matrix_sdk_test::{
1034        async_test, test_json,
1035        test_json::keys_query_sets::{
1036            IdentityChangeDataSet, KeyDistributionTestData, MaloIdentityChangeDataSet,
1037            VerificationViolationTestData,
1038        },
1039    };
1040    use ruma::{
1041        DeviceId, TransactionId, UserId, device_id,
1042        events::{dummy::ToDeviceDummyEventContent, room::history_visibility::HistoryVisibility},
1043        room_id,
1044    };
1045    use serde_json::json;
1046
1047    #[cfg(feature = "experimental-send-custom-to-device")]
1048    use super::split_devices_for_share_strategy;
1049    use crate::{
1050        CrossSigningKeyExport, DeviceData, EncryptionSettings, LocalTrust, OlmError, OlmMachine,
1051        error::SessionRecipientCollectionError,
1052        olm::{OutboundGroupSession, ShareInfo},
1053        session_manager::{
1054            CollectStrategy,
1055            group_sessions::share_strategy::{
1056                collect_session_recipients, withheld_code_for_device_for_share_strategy,
1057            },
1058        },
1059        store::caches::SequenceNumber,
1060        testing::simulate_key_query_response_for_verification,
1061        types::requests::ToDeviceRequest,
1062    };
1063
1064    /// Returns an `OlmMachine` set up for the test user in
1065    /// [`KeyDistributionTestData`], with cross-signing set up and the
1066    /// private cross-signing keys imported.
1067    async fn test_machine() -> OlmMachine {
1068        use KeyDistributionTestData as DataSet;
1069
1070        // Create the local user (`@me`), and import the public identity keys
1071        let machine = OlmMachine::new(DataSet::me_id(), DataSet::me_device_id()).await;
1072        let keys_query = DataSet::me_keys_query_response();
1073        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1074
1075        // Also import the private cross signing keys
1076        machine
1077            .import_cross_signing_keys(CrossSigningKeyExport {
1078                master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1079                self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1080                user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1081            })
1082            .await
1083            .unwrap();
1084
1085        machine
1086    }
1087
1088    /// Get the `DeviceData` struct for the given user's device.
1089    async fn get_device_data(
1090        machine: &OlmMachine,
1091        user_id: &UserId,
1092        device_id: &DeviceId,
1093    ) -> DeviceData {
1094        machine.get_device(user_id, device_id, None).await.unwrap().unwrap().deref().clone()
1095    }
1096
1097    async fn get_own_identity_data(
1098        machine: &OlmMachine,
1099        user_id: &UserId,
1100    ) -> Option<crate::OwnUserIdentityData> {
1101        machine
1102            .get_identity(user_id, None)
1103            .await
1104            .unwrap()
1105            .and_then(|i| i.own())
1106            .map(|i| i.deref().clone())
1107    }
1108
1109    async fn get_user_identity_data(
1110        machine: &OlmMachine,
1111        user_id: &UserId,
1112    ) -> Option<crate::UserIdentityData> {
1113        use crate::{UserIdentity, identities::user::UserIdentityData};
1114        machine.get_identity(user_id, None).await.unwrap().map(|i| match i {
1115            UserIdentity::Own(i) => UserIdentityData::Own(i.deref().clone()),
1116            UserIdentity::Other(i) => UserIdentityData::Other(i.deref().clone()),
1117        })
1118    }
1119
1120    /// Import device data for `@dan`, `@dave`, and `@good`, as referenced in
1121    /// [`KeyDistributionTestData`], into the given OlmMachine
1122    async fn import_known_users_to_test_machine(machine: &OlmMachine) {
1123        let keys_query = KeyDistributionTestData::dan_keys_query_response();
1124        let txn_id = TransactionId::new();
1125        machine.mark_request_as_sent(&txn_id, &keys_query).await.unwrap();
1126
1127        let txn_id_dave = TransactionId::new();
1128        let keys_query_dave = KeyDistributionTestData::dave_keys_query_response();
1129        machine.mark_request_as_sent(&txn_id_dave, &keys_query_dave).await.unwrap();
1130
1131        let txn_id_good = TransactionId::new();
1132        let keys_query_good = KeyDistributionTestData::good_keys_query_response();
1133        machine.mark_request_as_sent(&txn_id_good, &keys_query_good).await.unwrap();
1134    }
1135
1136    /// Assert that [`CollectStrategy::AllDevices`] retains the same
1137    /// serialization format.
1138    #[test]
1139    #[cfg(not(feature = "experimental-encrypted-state-events"))]
1140    fn test_serialize_device_based_strategy() {
1141        let encryption_settings = all_devices_strategy_settings();
1142        let serialized = serde_json::to_string(&encryption_settings).unwrap();
1143        with_settings!({prepend_module_to_snapshot => false}, {
1144            assert_snapshot!(serialized)
1145        });
1146    }
1147
1148    /// Assert that [`CollectStrategy::AllDevices`] retains the same
1149    /// serialization format, even when experimental encrypted state events
1150    /// are enabled.
1151    #[test]
1152    #[cfg(feature = "experimental-encrypted-state-events")]
1153    fn test_serialize_strategy_with_encrypted_state() {
1154        let encryption_settings = all_devices_strategy_settings();
1155        let serialized = serde_json::to_string(&encryption_settings).unwrap();
1156        with_settings!({prepend_module_to_snapshot => false}, {
1157            assert_snapshot!(serialized)
1158        });
1159    }
1160
1161    /// [`CollectStrategy::AllDevices`] used to be known as
1162    /// `DeviceBasedStrategy`. Check we can still deserialize the old
1163    /// representation.
1164    #[test]
1165    fn test_deserialize_old_device_based_strategy() {
1166        let settings: EncryptionSettings = serde_json::from_value(json!({
1167            "algorithm": "m.megolm.v1.aes-sha2",
1168            "rotation_period":{"secs":604800,"nanos":0},
1169            "rotation_period_msgs":100,
1170            "history_visibility":"shared",
1171            "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":false}},
1172        })).unwrap();
1173        assert_matches!(settings.sharing_strategy, CollectStrategy::AllDevices);
1174    }
1175
1176    /// [`CollectStrategy::ErrorOnVerifiedUserProblem`] used to be represented
1177    /// as a variant on the former `DeviceBasedStrategy`. Check we can still
1178    /// deserialize the old representation.
1179    #[test]
1180    fn test_deserialize_old_error_on_verified_user_problem() {
1181        let settings: EncryptionSettings = serde_json::from_value(json!({
1182            "algorithm": "m.megolm.v1.aes-sha2",
1183            "rotation_period":{"secs":604800,"nanos":0},
1184            "rotation_period_msgs":100,
1185            "history_visibility":"shared",
1186            "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":false,"error_on_verified_user_problem":true}},
1187        })).unwrap();
1188        assert_matches!(settings.sharing_strategy, CollectStrategy::ErrorOnVerifiedUserProblem);
1189    }
1190
1191    /// [`CollectStrategy::OnlyTrustedDevices`] used to be represented as a
1192    /// variant on the former `DeviceBasedStrategy`. Check we can still
1193    /// deserialize the old representation.
1194    #[test]
1195    fn test_deserialize_old_only_trusted_devices_strategy() {
1196        let settings: EncryptionSettings = serde_json::from_value(json!({
1197            "algorithm": "m.megolm.v1.aes-sha2",
1198            "rotation_period":{"secs":604800,"nanos":0},
1199            "rotation_period_msgs":100,
1200            "history_visibility":"shared",
1201            "sharing_strategy":{"DeviceBasedStrategy":{"only_allow_trusted_devices":true,"error_on_verified_user_problem":false}},
1202        })).unwrap();
1203        assert_matches!(settings.sharing_strategy, CollectStrategy::OnlyTrustedDevices);
1204    }
1205
1206    #[async_test]
1207    async fn test_share_with_per_device_strategy_to_all() {
1208        let machine = test_machine().await;
1209        import_known_users_to_test_machine(&machine).await;
1210
1211        let encryption_settings = all_devices_strategy_settings();
1212
1213        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1214
1215        let share_result = collect_session_recipients(
1216            machine.store(),
1217            vec![
1218                KeyDistributionTestData::dan_id(),
1219                KeyDistributionTestData::dave_id(),
1220                KeyDistributionTestData::good_id(),
1221            ]
1222            .into_iter(),
1223            &encryption_settings,
1224            &group_session,
1225        )
1226        .await
1227        .unwrap();
1228
1229        assert!(!share_result.should_rotate);
1230
1231        let dan_devices_shared =
1232            share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
1233        let dave_devices_shared =
1234            share_result.devices.get(KeyDistributionTestData::dave_id()).unwrap();
1235        let good_devices_shared =
1236            share_result.devices.get(KeyDistributionTestData::good_id()).unwrap();
1237
1238        // With this strategy the room key would be distributed to all devices
1239        assert_eq!(dan_devices_shared.len(), 2);
1240        assert_eq!(dave_devices_shared.len(), 1);
1241        assert_eq!(good_devices_shared.len(), 2);
1242
1243        #[cfg(feature = "experimental-send-custom-to-device")]
1244        {
1245            // construct the list of all devices from the result of
1246            // collect_session_recipients, because that gives us the devices as
1247            // `DeviceData`
1248            let mut all_devices = dan_devices_shared.clone();
1249            all_devices.append(&mut dave_devices_shared.clone());
1250            all_devices.append(&mut good_devices_shared.clone());
1251
1252            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1253                machine.store(),
1254                all_devices,
1255                CollectStrategy::AllDevices,
1256            )
1257            .await
1258            .unwrap();
1259
1260            assert_eq!(shared_devices.len(), 5);
1261            assert_eq!(withheld_devices.len(), 0);
1262        }
1263
1264        let own_identity_data =
1265            get_own_identity_data(&machine, KeyDistributionTestData::me_id()).await;
1266        let dan_identity_data =
1267            get_user_identity_data(&machine, KeyDistributionTestData::dan_id()).await;
1268
1269        assert_eq!(
1270            withheld_code_for_device_for_share_strategy(
1271                &get_device_data(
1272                    &machine,
1273                    KeyDistributionTestData::dan_id(),
1274                    KeyDistributionTestData::dan_signed_device_id()
1275                )
1276                .await,
1277                CollectStrategy::AllDevices,
1278                &own_identity_data,
1279                &dan_identity_data,
1280            )
1281            .await
1282            .unwrap(),
1283            None,
1284        );
1285    }
1286
1287    #[async_test]
1288    async fn test_share_with_only_trusted_strategy() {
1289        let machine = test_machine().await;
1290        import_known_users_to_test_machine(&machine).await;
1291
1292        let encryption_settings = EncryptionSettings {
1293            sharing_strategy: CollectStrategy::OnlyTrustedDevices,
1294            ..Default::default()
1295        };
1296
1297        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1298
1299        let share_result = collect_session_recipients(
1300            machine.store(),
1301            vec![
1302                KeyDistributionTestData::dan_id(),
1303                KeyDistributionTestData::dave_id(),
1304                KeyDistributionTestData::good_id(),
1305            ]
1306            .into_iter(),
1307            &encryption_settings,
1308            &group_session,
1309        )
1310        .await
1311        .unwrap();
1312
1313        assert!(!share_result.should_rotate);
1314
1315        let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
1316        let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
1317        // dave and good wouldn't receive any key
1318        assert!(dave_devices_shared.unwrap().is_empty());
1319        assert!(good_devices_shared.unwrap().is_empty());
1320
1321        // dan is verified by me and has one of his devices self signed, so should get
1322        // the key
1323        let dan_devices_shared =
1324            share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
1325
1326        assert_eq!(dan_devices_shared.len(), 1);
1327        let dan_device_that_will_get_the_key = &dan_devices_shared[0];
1328        assert_eq!(
1329            dan_device_that_will_get_the_key.device_id().as_str(),
1330            KeyDistributionTestData::dan_signed_device_id()
1331        );
1332
1333        // Check withhelds for others
1334        let (_, code) = share_result
1335            .withheld_devices
1336            .iter()
1337            .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
1338            .expect("This dan's device should receive a withheld code");
1339
1340        assert_eq!(code, &WithheldCode::Unverified);
1341
1342        let (_, code) = share_result
1343            .withheld_devices
1344            .iter()
1345            .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
1346            .expect("This daves's device should receive a withheld code");
1347
1348        assert_eq!(code, &WithheldCode::Unverified);
1349
1350        #[cfg(feature = "experimental-send-custom-to-device")]
1351        {
1352            let all_devices: Vec<DeviceData> = vec![
1353                get_device_data(
1354                    &machine,
1355                    KeyDistributionTestData::dan_id(),
1356                    KeyDistributionTestData::dan_unsigned_device_id(),
1357                )
1358                .await,
1359                get_device_data(
1360                    &machine,
1361                    KeyDistributionTestData::dan_id(),
1362                    KeyDistributionTestData::dan_signed_device_id(),
1363                )
1364                .await,
1365                get_device_data(
1366                    &machine,
1367                    KeyDistributionTestData::dave_id(),
1368                    KeyDistributionTestData::dave_device_id(),
1369                )
1370                .await,
1371                get_device_data(
1372                    &machine,
1373                    KeyDistributionTestData::good_id(),
1374                    KeyDistributionTestData::good_device_1_id(),
1375                )
1376                .await,
1377                get_device_data(
1378                    &machine,
1379                    KeyDistributionTestData::good_id(),
1380                    KeyDistributionTestData::good_device_2_id(),
1381                )
1382                .await,
1383            ];
1384
1385            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1386                machine.store(),
1387                all_devices,
1388                CollectStrategy::OnlyTrustedDevices,
1389            )
1390            .await
1391            .unwrap();
1392
1393            assert_eq!(shared_devices.len(), 1);
1394            assert_eq!(
1395                shared_devices[0].device_id().as_str(),
1396                KeyDistributionTestData::dan_signed_device_id()
1397            );
1398
1399            assert_eq!(withheld_devices.len(), 4);
1400            assert_eq!(
1401                withheld_devices[0].0.device_id().as_str(),
1402                KeyDistributionTestData::dan_unsigned_device_id()
1403            );
1404            assert_eq!(withheld_devices[0].1, WithheldCode::Unverified);
1405            assert_eq!(
1406                withheld_devices[1].0.device_id().as_str(),
1407                KeyDistributionTestData::dave_device_id()
1408            );
1409            assert_eq!(withheld_devices[1].1, WithheldCode::Unverified);
1410        }
1411
1412        let own_identity_data =
1413            get_own_identity_data(&machine, KeyDistributionTestData::me_id()).await;
1414        let dan_identity_data =
1415            get_user_identity_data(&machine, KeyDistributionTestData::dan_id()).await;
1416        let dave_identity_data =
1417            get_user_identity_data(&machine, KeyDistributionTestData::dave_id()).await;
1418
1419        assert_eq!(
1420            withheld_code_for_device_for_share_strategy(
1421                &get_device_data(
1422                    &machine,
1423                    KeyDistributionTestData::dan_id(),
1424                    KeyDistributionTestData::dan_signed_device_id()
1425                )
1426                .await,
1427                CollectStrategy::OnlyTrustedDevices,
1428                &own_identity_data,
1429                &dan_identity_data,
1430            )
1431            .await
1432            .unwrap(),
1433            None,
1434        );
1435        assert_eq!(
1436            withheld_code_for_device_for_share_strategy(
1437                &get_device_data(
1438                    &machine,
1439                    KeyDistributionTestData::dan_id(),
1440                    KeyDistributionTestData::dan_unsigned_device_id()
1441                )
1442                .await,
1443                CollectStrategy::OnlyTrustedDevices,
1444                &own_identity_data,
1445                &dan_identity_data,
1446            )
1447            .await
1448            .unwrap(),
1449            Some(WithheldCode::Unverified),
1450        );
1451        assert_eq!(
1452            withheld_code_for_device_for_share_strategy(
1453                &get_device_data(
1454                    &machine,
1455                    KeyDistributionTestData::dave_id(),
1456                    KeyDistributionTestData::dave_device_id()
1457                )
1458                .await,
1459                CollectStrategy::OnlyTrustedDevices,
1460                &own_identity_data,
1461                &dave_identity_data,
1462            )
1463            .await
1464            .unwrap(),
1465            Some(WithheldCode::Unverified),
1466        );
1467    }
1468
1469    /// Test that [`collect_session_recipients`] returns an error if there are
1470    /// unsigned devices belonging to verified users, when
1471    /// `error_on_verified_user_problem` is set.
1472    #[async_test]
1473    async fn test_error_on_unsigned_of_verified_users() {
1474        use VerificationViolationTestData as DataSet;
1475
1476        // We start with Bob, who is verified and has one unsigned device.
1477        let machine = unsigned_of_verified_setup().await;
1478
1479        // Add Carol, also verified with one unsigned device.
1480        let carol_keys = DataSet::carol_keys_query_response_signed();
1481        machine.mark_request_as_sent(&TransactionId::new(), &carol_keys).await.unwrap();
1482
1483        // Double-check the state of Carol.
1484        let carol_identity =
1485            machine.get_identity(DataSet::carol_id(), None).await.unwrap().unwrap();
1486        assert!(carol_identity.other().unwrap().is_verified());
1487
1488        let carol_unsigned_device = machine
1489            .get_device(DataSet::carol_id(), DataSet::carol_unsigned_device_id(), None)
1490            .await
1491            .unwrap()
1492            .unwrap();
1493        assert!(!carol_unsigned_device.is_verified());
1494
1495        // Sharing an OutboundGroupSession should fail.
1496        let encryption_settings = error_on_verification_problem_encryption_settings();
1497        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1498        let share_result = collect_session_recipients(
1499            machine.store(),
1500            vec![DataSet::bob_id(), DataSet::carol_id()].into_iter(),
1501            &encryption_settings,
1502            &group_session,
1503        )
1504        .await;
1505
1506        assert_let!(
1507            Err(OlmError::SessionRecipientCollectionError(
1508                SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
1509            )) = share_result
1510        );
1511
1512        // Check the list of devices in the error.
1513        assert_eq!(
1514            unverified_devices,
1515            BTreeMap::from([
1516                (DataSet::bob_id().to_owned(), vec![DataSet::bob_device_2_id().to_owned()]),
1517                (
1518                    DataSet::carol_id().to_owned(),
1519                    vec![DataSet::carol_unsigned_device_id().to_owned()]
1520                ),
1521            ])
1522        );
1523
1524        #[cfg(feature = "experimental-send-custom-to-device")]
1525        {
1526            let all_devices = vec![
1527                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
1528                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1529                get_device_data(&machine, DataSet::carol_id(), DataSet::carol_signed_device_id())
1530                    .await,
1531                get_device_data(&machine, DataSet::carol_id(), DataSet::carol_unsigned_device_id())
1532                    .await,
1533            ];
1534
1535            let split_result = split_devices_for_share_strategy(
1536                machine.store(),
1537                all_devices,
1538                CollectStrategy::ErrorOnVerifiedUserProblem,
1539            )
1540            .await;
1541
1542            assert_let!(
1543                Err(OlmError::SessionRecipientCollectionError(
1544                    SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
1545                        unverified_devices
1546                    )
1547                )) = split_result
1548            );
1549
1550            // Check the list of devices in the error.
1551            assert_eq!(
1552                unverified_devices,
1553                BTreeMap::from([
1554                    (DataSet::bob_id().to_owned(), vec![DataSet::bob_device_2_id().to_owned()]),
1555                    (
1556                        DataSet::carol_id().to_owned(),
1557                        vec![DataSet::carol_unsigned_device_id().to_owned()]
1558                    ),
1559                ])
1560            );
1561        }
1562
1563        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1564        let carol_identity_data = get_user_identity_data(&machine, DataSet::carol_id()).await;
1565
1566        assert_eq!(
1567            withheld_code_for_device_for_share_strategy(
1568                &get_device_data(&machine, DataSet::carol_id(), DataSet::carol_signed_device_id())
1569                    .await,
1570                CollectStrategy::ErrorOnVerifiedUserProblem,
1571                &own_identity_data,
1572                &carol_identity_data,
1573            )
1574            .await
1575            .unwrap(),
1576            None,
1577        );
1578        assert_let!(
1579            Err(OlmError::SessionRecipientCollectionError(
1580                SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(_)
1581            )) = withheld_code_for_device_for_share_strategy(
1582                &get_device_data(
1583                    &machine,
1584                    DataSet::carol_id(),
1585                    DataSet::carol_unsigned_device_id()
1586                )
1587                .await,
1588                CollectStrategy::ErrorOnVerifiedUserProblem,
1589                &own_identity_data,
1590                &carol_identity_data,
1591            )
1592            .await
1593        );
1594    }
1595
1596    /// Test that we can resolve errors from
1597    /// `error_on_verified_user_problem` by whitelisting the
1598    /// device.
1599    #[async_test]
1600    async fn test_error_on_unsigned_of_verified_resolve_by_whitelisting() {
1601        use VerificationViolationTestData as DataSet;
1602
1603        let machine = unsigned_of_verified_setup().await;
1604
1605        // Whitelist the unsigned device
1606        machine
1607            .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1608            .await
1609            .unwrap()
1610            .unwrap()
1611            .set_local_trust(LocalTrust::Ignored)
1612            .await
1613            .unwrap();
1614
1615        let encryption_settings = error_on_verification_problem_encryption_settings();
1616        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1617
1618        // We should be able to share a key, and it should include the unsigned device.
1619        let share_result = collect_session_recipients(
1620            machine.store(),
1621            iter::once(DataSet::bob_id()),
1622            &encryption_settings,
1623            &group_session,
1624        )
1625        .await
1626        .unwrap();
1627
1628        assert_eq!(2, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1629        assert_eq!(0, share_result.withheld_devices.len());
1630
1631        #[cfg(feature = "experimental-send-custom-to-device")]
1632        {
1633            let all_devices = vec![
1634                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
1635                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1636            ];
1637
1638            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1639                machine.store(),
1640                all_devices,
1641                CollectStrategy::ErrorOnVerifiedUserProblem,
1642            )
1643            .await
1644            .unwrap();
1645
1646            assert_eq!(shared_devices.len(), 2);
1647            assert_eq!(withheld_devices.len(), 0);
1648        }
1649
1650        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1651        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
1652
1653        assert_eq!(
1654            withheld_code_for_device_for_share_strategy(
1655                &get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1656                CollectStrategy::ErrorOnVerifiedUserProblem,
1657                &own_identity_data,
1658                &bob_identity_data,
1659            )
1660            .await
1661            .unwrap(),
1662            None,
1663        );
1664    }
1665
1666    /// Test that we can resolve errors from
1667    /// `error_on_verified_user_problem` by blacklisting the
1668    /// device.
1669    #[async_test]
1670    async fn test_error_on_unsigned_of_verified_resolve_by_blacklisting() {
1671        use VerificationViolationTestData as DataSet;
1672
1673        let machine = unsigned_of_verified_setup().await;
1674
1675        // Blacklist the unsigned device
1676        machine
1677            .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1678            .await
1679            .unwrap()
1680            .unwrap()
1681            .set_local_trust(LocalTrust::BlackListed)
1682            .await
1683            .unwrap();
1684
1685        let encryption_settings = error_on_verification_problem_encryption_settings();
1686        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1687
1688        // We should be able to share a key, and it should exclude the unsigned device.
1689        let share_result = collect_session_recipients(
1690            machine.store(),
1691            iter::once(DataSet::bob_id()),
1692            &encryption_settings,
1693            &group_session,
1694        )
1695        .await
1696        .unwrap();
1697
1698        assert_eq!(1, share_result.devices.get(DataSet::bob_id()).unwrap().len());
1699        let withheld_list: Vec<_> = share_result
1700            .withheld_devices
1701            .iter()
1702            .map(|(d, code)| (d.device_id().to_owned(), code.clone()))
1703            .collect();
1704        assert_eq!(
1705            withheld_list,
1706            vec![(DataSet::bob_device_2_id().to_owned(), WithheldCode::Blacklisted)]
1707        );
1708
1709        let bob_device_2 =
1710            get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await;
1711        #[cfg(feature = "experimental-send-custom-to-device")]
1712        {
1713            let bob_device_1 =
1714                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await;
1715            let all_devices = vec![bob_device_1.clone(), bob_device_2.clone()];
1716
1717            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1718                machine.store(),
1719                all_devices,
1720                CollectStrategy::ErrorOnVerifiedUserProblem,
1721            )
1722            .await
1723            .unwrap();
1724
1725            assert_eq!(shared_devices, vec![bob_device_1.clone()]);
1726            assert_eq!(withheld_devices, vec![(bob_device_2.clone(), WithheldCode::Blacklisted)]);
1727        }
1728
1729        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1730        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
1731
1732        assert_eq!(
1733            withheld_code_for_device_for_share_strategy(
1734                &bob_device_2,
1735                CollectStrategy::ErrorOnVerifiedUserProblem,
1736                &own_identity_data,
1737                &bob_identity_data,
1738            )
1739            .await
1740            .unwrap(),
1741            Some(WithheldCode::Blacklisted),
1742        );
1743    }
1744
1745    /// Test that [`collect_session_recipients`] returns an error when
1746    /// `error_on_verified_user_problem` is set, if our own identity
1747    /// is verified and we have unsigned devices.
1748    #[async_test]
1749    async fn test_error_on_unsigned_of_verified_owner_is_us() {
1750        use VerificationViolationTestData as DataSet;
1751
1752        let machine = unsigned_of_verified_setup().await;
1753
1754        // Add a couple of devices to Alice's account
1755        let mut own_keys = DataSet::own_keys_query_response_1().clone();
1756        own_keys.device_keys.insert(
1757            DataSet::own_id().to_owned(),
1758            BTreeMap::from([
1759                DataSet::own_signed_device_keys(),
1760                DataSet::own_unsigned_device_keys(),
1761            ]),
1762        );
1763        machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1764
1765        let encryption_settings = error_on_verification_problem_encryption_settings();
1766        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1767        let share_result = collect_session_recipients(
1768            machine.store(),
1769            iter::once(DataSet::own_id()),
1770            &encryption_settings,
1771            &group_session,
1772        )
1773        .await;
1774
1775        assert_let!(
1776            Err(OlmError::SessionRecipientCollectionError(
1777                SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(unverified_devices)
1778            )) = share_result
1779        );
1780
1781        // Check the list of devices in the error.
1782        assert_eq!(
1783            unverified_devices,
1784            BTreeMap::from([(
1785                DataSet::own_id().to_owned(),
1786                vec![DataSet::own_unsigned_device_id()]
1787            ),])
1788        );
1789
1790        #[cfg(feature = "experimental-send-custom-to-device")]
1791        {
1792            let all_devices = vec![
1793                get_device_data(&machine, DataSet::own_id(), &DataSet::own_signed_device_id())
1794                    .await,
1795                get_device_data(&machine, DataSet::own_id(), &DataSet::own_unsigned_device_id())
1796                    .await,
1797            ];
1798
1799            let split_result = split_devices_for_share_strategy(
1800                machine.store(),
1801                all_devices,
1802                CollectStrategy::ErrorOnVerifiedUserProblem,
1803            )
1804            .await;
1805            assert_let!(
1806                Err(OlmError::SessionRecipientCollectionError(
1807                    SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(
1808                        unverified_devices
1809                    )
1810                )) = split_result
1811            );
1812
1813            // Check the list of devices in the error.
1814            assert_eq!(
1815                unverified_devices,
1816                BTreeMap::from([(
1817                    DataSet::own_id().to_owned(),
1818                    vec![DataSet::own_unsigned_device_id()]
1819                ),])
1820            );
1821        }
1822
1823        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1824        let own_user_identity_data = get_user_identity_data(&machine, DataSet::own_id()).await;
1825
1826        assert_let!(
1827            Err(OlmError::SessionRecipientCollectionError(
1828                SessionRecipientCollectionError::VerifiedUserHasUnsignedDevice(_)
1829            )) = withheld_code_for_device_for_share_strategy(
1830                &get_device_data(&machine, DataSet::own_id(), &DataSet::own_unsigned_device_id())
1831                    .await,
1832                CollectStrategy::ErrorOnVerifiedUserProblem,
1833                &own_identity_data,
1834                &own_user_identity_data,
1835            )
1836            .await
1837        );
1838    }
1839
1840    /// Test that an unsigned device of an unverified user doesn't cause an
1841    /// error.
1842    #[async_test]
1843    async fn test_should_not_error_on_unsigned_of_unverified() {
1844        use VerificationViolationTestData as DataSet;
1845
1846        let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1847
1848        // Tell the OlmMachine about our own public keys.
1849        let own_keys = DataSet::own_keys_query_response_1();
1850        machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
1851
1852        // Import the secret parts of our own cross-signing keys.
1853        machine
1854            .import_cross_signing_keys(CrossSigningKeyExport {
1855                master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
1856                self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1857                user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
1858            })
1859            .await
1860            .unwrap();
1861
1862        // This time our own identity is trusted but is not signing bob.
1863        let bob_keys = DataSet::bob_keys_query_response_rotated();
1864        machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
1865
1866        // Double-check the state of Bob: he should be unverified, and should have an
1867        // unsigned device.
1868        let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
1869        assert!(!bob_identity.other().unwrap().is_verified());
1870
1871        let bob_unsigned_device = machine
1872            .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
1873            .await
1874            .unwrap()
1875            .unwrap();
1876        assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1877
1878        let encryption_settings = error_on_verification_problem_encryption_settings();
1879        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1880        collect_session_recipients(
1881            machine.store(),
1882            iter::once(DataSet::bob_id()),
1883            &encryption_settings,
1884            &group_session,
1885        )
1886        .await
1887        .unwrap();
1888
1889        #[cfg(feature = "experimental-send-custom-to-device")]
1890        {
1891            let all_devices = vec![
1892                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
1893                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1894            ];
1895
1896            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1897                machine.store(),
1898                all_devices,
1899                CollectStrategy::ErrorOnVerifiedUserProblem,
1900            )
1901            .await
1902            .unwrap();
1903
1904            assert_eq!(shared_devices.len(), 2);
1905            assert_eq!(withheld_devices.len(), 0);
1906        }
1907
1908        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1909        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
1910
1911        assert_eq!(
1912            withheld_code_for_device_for_share_strategy(
1913                &get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1914                CollectStrategy::ErrorOnVerifiedUserProblem,
1915                &own_identity_data,
1916                &bob_identity_data,
1917            )
1918            .await
1919            .unwrap(),
1920            None,
1921        );
1922    }
1923
1924    /// Test that an unsigned device of a signed user doesn't cause an
1925    /// error, when we have not verified our own identity.
1926    #[async_test]
1927    async fn test_should_not_error_on_unsigned_of_signed_but_unverified() {
1928        use VerificationViolationTestData as DataSet;
1929
1930        let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
1931
1932        // Tell the OlmMachine about our own public keys.
1933        let keys_query = DataSet::own_keys_query_response_1();
1934        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1935
1936        // ... and those of Bob.
1937        let keys_query = DataSet::bob_keys_query_response_signed();
1938        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
1939
1940        // Double-check the state of Bob: his identity should be signed but unverified,
1941        // and he should have an unsigned device.
1942        let bob_identity =
1943            machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap().other().unwrap();
1944        assert!(
1945            bob_identity.own_identity.as_ref().unwrap().is_identity_signed(&bob_identity.inner)
1946        );
1947        assert!(!bob_identity.is_verified());
1948
1949        let bob_unsigned_device = machine
1950            .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
1951            .await
1952            .unwrap()
1953            .unwrap();
1954        assert!(!bob_unsigned_device.is_cross_signed_by_owner());
1955
1956        // Share a session, and ensure that it doesn't error.
1957        let encryption_settings = error_on_verification_problem_encryption_settings();
1958        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
1959        collect_session_recipients(
1960            machine.store(),
1961            iter::once(DataSet::bob_id()),
1962            &encryption_settings,
1963            &group_session,
1964        )
1965        .await
1966        .unwrap();
1967
1968        #[cfg(feature = "experimental-send-custom-to-device")]
1969        {
1970            let all_devices = vec![
1971                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
1972                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1973            ];
1974
1975            let (shared_devices, withheld_devices) = split_devices_for_share_strategy(
1976                machine.store(),
1977                all_devices,
1978                CollectStrategy::ErrorOnVerifiedUserProblem,
1979            )
1980            .await
1981            .unwrap();
1982
1983            assert_eq!(shared_devices.len(), 2);
1984            assert_eq!(withheld_devices.len(), 0);
1985        }
1986
1987        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
1988        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
1989
1990        assert_eq!(
1991            withheld_code_for_device_for_share_strategy(
1992                &get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
1993                CollectStrategy::ErrorOnVerifiedUserProblem,
1994                &own_identity_data,
1995                &bob_identity_data,
1996            )
1997            .await
1998            .unwrap(),
1999            None,
2000        );
2001    }
2002
2003    /// Test that a verified user changing their identity causes an error in
2004    /// `collect_session_recipients`, and that it can be resolved by
2005    /// withdrawing verification
2006    #[async_test]
2007    async fn test_verified_user_changed_identity() {
2008        use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
2009
2010        // We start with Bob, who is verified and has one unsigned device. We have also
2011        // verified our own identity.
2012        let machine = unsigned_of_verified_setup().await;
2013
2014        // Bob then rotates his identity
2015        let bob_keys = DataSet::bob_keys_query_response_rotated();
2016        machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
2017
2018        // Double-check the state of Bob
2019        let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
2020        assert!(bob_identity.has_verification_violation());
2021
2022        // Sharing an OutboundGroupSession should fail.
2023        let encryption_settings = error_on_verification_problem_encryption_settings();
2024        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2025        let share_result = collect_session_recipients(
2026            machine.store(),
2027            iter::once(DataSet::bob_id()),
2028            &encryption_settings,
2029            &group_session,
2030        )
2031        .await;
2032
2033        assert_let!(
2034            Err(OlmError::SessionRecipientCollectionError(
2035                SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
2036            )) = share_result
2037        );
2038        assert_eq!(violating_users, vec![DataSet::bob_id()]);
2039
2040        #[cfg(feature = "experimental-send-custom-to-device")]
2041        {
2042            let all_devices = vec![
2043                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
2044                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
2045            ];
2046
2047            let split_result = split_devices_for_share_strategy(
2048                machine.store(),
2049                all_devices,
2050                CollectStrategy::ErrorOnVerifiedUserProblem,
2051            )
2052            .await;
2053            assert_let!(
2054                Err(OlmError::SessionRecipientCollectionError(
2055                    SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
2056                )) = split_result
2057            );
2058            assert_eq!(violating_users, vec![DataSet::bob_id()]);
2059        }
2060
2061        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
2062        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
2063
2064        assert_let!(
2065            Err(OlmError::SessionRecipientCollectionError(
2066                SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
2067            )) = withheld_code_for_device_for_share_strategy(
2068                &get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
2069                CollectStrategy::ErrorOnVerifiedUserProblem,
2070                &own_identity_data,
2071                &bob_identity_data,
2072            )
2073            .await
2074        );
2075
2076        // Resolve by calling withdraw_verification
2077        bob_identity.withdraw_verification().await.unwrap();
2078
2079        collect_session_recipients(
2080            machine.store(),
2081            iter::once(DataSet::bob_id()),
2082            &encryption_settings,
2083            &group_session,
2084        )
2085        .await
2086        .unwrap();
2087
2088        #[cfg(feature = "experimental-send-custom-to-device")]
2089        {
2090            let all_devices = vec![
2091                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
2092                get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_2_id()).await,
2093            ];
2094
2095            split_devices_for_share_strategy(
2096                machine.store(),
2097                all_devices,
2098                CollectStrategy::ErrorOnVerifiedUserProblem,
2099            )
2100            .await
2101            .unwrap();
2102        }
2103
2104        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
2105        let bob_identity_data = get_user_identity_data(&machine, DataSet::bob_id()).await;
2106
2107        assert_eq!(
2108            withheld_code_for_device_for_share_strategy(
2109                &get_device_data(&machine, DataSet::bob_id(), DataSet::bob_device_1_id()).await,
2110                CollectStrategy::ErrorOnVerifiedUserProblem,
2111                &own_identity_data,
2112                &bob_identity_data,
2113            )
2114            .await
2115            .unwrap(),
2116            None,
2117        );
2118    }
2119
2120    /// Test that our own identity being changed causes an error in
2121    /// `collect_session_recipients`, and that it can be resolved by
2122    /// withdrawing verification
2123    #[async_test]
2124    async fn test_own_verified_identity_changed() {
2125        use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
2126
2127        // We start with a verified identity.
2128        let machine = unsigned_of_verified_setup().await;
2129        let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
2130        assert!(own_identity.own().unwrap().is_verified());
2131
2132        // Another device rotates our own identity.
2133        let own_keys = DataSet::own_keys_query_response_2();
2134        machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
2135
2136        let own_identity = machine.get_identity(DataSet::own_id(), None).await.unwrap().unwrap();
2137        assert!(!own_identity.is_verified());
2138
2139        // Sharing an OutboundGroupSession should fail.
2140        let encryption_settings = error_on_verification_problem_encryption_settings();
2141        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2142        let share_result = collect_session_recipients(
2143            machine.store(),
2144            iter::once(DataSet::own_id()),
2145            &encryption_settings,
2146            &group_session,
2147        )
2148        .await;
2149
2150        assert_let!(
2151            Err(OlmError::SessionRecipientCollectionError(
2152                SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
2153            )) = share_result
2154        );
2155        assert_eq!(violating_users, vec![DataSet::own_id()]);
2156
2157        #[cfg(feature = "experimental-send-custom-to-device")]
2158        {
2159            let all_devices: Vec<DeviceData> =
2160                vec![get_device_data(&machine, DataSet::own_id(), machine.device_id()).await];
2161
2162            let split_result = split_devices_for_share_strategy(
2163                machine.store(),
2164                all_devices,
2165                CollectStrategy::ErrorOnVerifiedUserProblem,
2166            )
2167            .await;
2168
2169            assert_let!(
2170                Err(OlmError::SessionRecipientCollectionError(
2171                    SessionRecipientCollectionError::VerifiedUserChangedIdentity(violating_users)
2172                )) = split_result
2173            );
2174            assert_eq!(violating_users, vec![DataSet::own_id()]);
2175        }
2176
2177        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
2178        let own_user_identity_data = get_user_identity_data(&machine, DataSet::own_id()).await;
2179
2180        assert_let!(
2181            Err(OlmError::SessionRecipientCollectionError(
2182                SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
2183            )) = withheld_code_for_device_for_share_strategy(
2184                &get_device_data(&machine, DataSet::own_id(), machine.device_id()).await,
2185                CollectStrategy::ErrorOnVerifiedUserProblem,
2186                &own_identity_data,
2187                &own_user_identity_data,
2188            )
2189            .await
2190        );
2191
2192        // Resolve by calling withdraw_verification
2193        own_identity.withdraw_verification().await.unwrap();
2194
2195        collect_session_recipients(
2196            machine.store(),
2197            iter::once(DataSet::own_id()),
2198            &encryption_settings,
2199            &group_session,
2200        )
2201        .await
2202        .unwrap();
2203
2204        #[cfg(feature = "experimental-send-custom-to-device")]
2205        {
2206            let all_devices: Vec<DeviceData> =
2207                vec![get_device_data(&machine, DataSet::own_id(), machine.device_id()).await];
2208
2209            split_devices_for_share_strategy(
2210                machine.store(),
2211                all_devices,
2212                CollectStrategy::ErrorOnVerifiedUserProblem,
2213            )
2214            .await
2215            .unwrap();
2216        }
2217
2218        let own_identity_data = get_own_identity_data(&machine, DataSet::own_id()).await;
2219        let own_user_identity_data = get_user_identity_data(&machine, DataSet::own_id()).await;
2220
2221        withheld_code_for_device_for_share_strategy(
2222            &get_device_data(&machine, DataSet::own_id(), machine.device_id()).await,
2223            CollectStrategy::ErrorOnVerifiedUserProblem,
2224            &own_identity_data,
2225            &own_user_identity_data,
2226        )
2227        .await
2228        .unwrap();
2229    }
2230
2231    /// A set of tests for the behaviour of [`collect_session_recipients`] with
2232    /// a dehydrated device
2233    mod dehydrated_device {
2234        use std::{collections::HashSet, iter};
2235
2236        use insta::{allow_duplicates, assert_json_snapshot, with_settings};
2237        use matrix_sdk_common::deserialized_responses::WithheldCode;
2238        use matrix_sdk_test::{
2239            async_test, ruma_response_to_json,
2240            test_json::keys_query_sets::{
2241                KeyDistributionTestData, KeyQueryResponseTemplate,
2242                KeyQueryResponseTemplateDeviceOptions,
2243            },
2244        };
2245        use ruma::{DeviceId, TransactionId, UserId, device_id, user_id};
2246        use vodozemac::{Curve25519PublicKey, Ed25519SecretKey};
2247
2248        use super::{
2249            all_devices_strategy_settings, create_test_outbound_group_session,
2250            error_on_verification_problem_encryption_settings, identity_based_strategy_settings,
2251            test_machine,
2252        };
2253        use crate::{
2254            EncryptionSettings, OlmMachine,
2255            session_manager::group_sessions::{
2256                CollectRecipientsResult, share_strategy::collect_session_recipients,
2257            },
2258        };
2259
2260        #[async_test]
2261        async fn test_all_devices_strategy_should_share_with_verified_dehydrated_device() {
2262            should_share_with_verified_dehydrated_device(&all_devices_strategy_settings()).await
2263        }
2264
2265        #[async_test]
2266        async fn test_error_on_verification_problem_strategy_should_share_with_verified_dehydrated_device()
2267         {
2268            should_share_with_verified_dehydrated_device(
2269                &error_on_verification_problem_encryption_settings(),
2270            )
2271            .await
2272        }
2273
2274        #[async_test]
2275        async fn test_identity_based_strategy_should_share_with_verified_dehydrated_device() {
2276            should_share_with_verified_dehydrated_device(&identity_based_strategy_settings()).await
2277        }
2278
2279        /// Common helper for
2280        /// [`test_all_devices_strategy_should_share_with_verified_dehydrated_device`],
2281        /// [`test_error_on_verification_problem_strategy_should_share_with_verified_dehydrated_device`]
2282        /// and [`test_identity_based_strategy_should_share_with_verified_dehydrated_device`].
2283        async fn should_share_with_verified_dehydrated_device(
2284            encryption_settings: &EncryptionSettings,
2285        ) {
2286            let machine = test_machine().await;
2287
2288            // Bob is a user with cross-signing, who has a single (verified) dehydrated
2289            // device.
2290            let bob_user_id = user_id!("@bob:localhost");
2291            let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
2292            let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
2293                .with_dehydrated_device(bob_dehydrated_device_id, true)
2294                .build_response();
2295            allow_duplicates! {
2296                with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2297                    assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
2298                });
2299            }
2300            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2301
2302            // When we collect the recipients ...
2303            let recips = share_test_session_and_collect_recipients(
2304                &machine,
2305                bob_user_id,
2306                encryption_settings,
2307            )
2308            .await;
2309
2310            // ... then the dehydrated device should be included
2311            assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
2312        }
2313
2314        #[async_test]
2315        async fn test_all_devices_strategy_should_not_share_with_unverified_dehydrated_device() {
2316            should_not_share_with_unverified_dehydrated_device(&all_devices_strategy_settings())
2317                .await
2318        }
2319
2320        #[async_test]
2321        async fn test_error_on_verification_problem_strategy_should_not_share_with_unverified_dehydrated_device()
2322         {
2323            should_not_share_with_unverified_dehydrated_device(
2324                &error_on_verification_problem_encryption_settings(),
2325            )
2326            .await
2327        }
2328
2329        #[async_test]
2330        async fn test_identity_based_strategy_should_not_share_with_unverified_dehydrated_device() {
2331            should_not_share_with_unverified_dehydrated_device(&identity_based_strategy_settings())
2332                .await
2333        }
2334
2335        /// Common helper for
2336        /// [`test_all_devices_strategy_should_not_share_with_unverified_dehydrated_device`],
2337        /// [`test_error_on_verification_problem_strategy_should_not_share_with_unverified_dehydrated_device`]
2338        /// and [`test_identity_based_strategy_should_not_share_with_unverified_dehydrated_device`].
2339        async fn should_not_share_with_unverified_dehydrated_device(
2340            encryption_settings: &EncryptionSettings,
2341        ) {
2342            let machine = test_machine().await;
2343
2344            // Bob is a user with cross-signing, who has a single (unverified) dehydrated
2345            // device.
2346            let bob_user_id = user_id!("@bob:localhost");
2347            let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
2348            let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
2349                .with_dehydrated_device(bob_dehydrated_device_id, false)
2350                .build_response();
2351            allow_duplicates! {
2352                with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2353                    assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
2354                });
2355            }
2356            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2357
2358            // When we collect the recipients ...
2359            let recips = share_test_session_and_collect_recipients(
2360                &machine,
2361                bob_user_id,
2362                encryption_settings,
2363            )
2364            .await;
2365
2366            // ... it shouldn't be shared with anyone, and there should be a withheld
2367            // message for the dehydrated device.
2368            assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
2369        }
2370
2371        #[async_test]
2372        async fn test_all_devices_strategy_should_share_with_verified_device_of_pin_violation_user()
2373        {
2374            should_share_with_verified_device_of_pin_violation_user(
2375                &all_devices_strategy_settings(),
2376            )
2377            .await
2378        }
2379
2380        #[async_test]
2381        async fn test_error_on_verification_problem_strategy_should_share_with_verified_device_of_pin_violation_user()
2382         {
2383            should_share_with_verified_device_of_pin_violation_user(
2384                &error_on_verification_problem_encryption_settings(),
2385            )
2386            .await
2387        }
2388
2389        #[async_test]
2390        async fn test_identity_based_strategy_should_share_with_verified_device_of_pin_violation_user()
2391         {
2392            should_share_with_verified_device_of_pin_violation_user(
2393                &identity_based_strategy_settings(),
2394            )
2395            .await
2396        }
2397
2398        /// Common helper for
2399        /// [`test_all_devices_strategy_should_share_with_verified_device_of_pin_violation_user`],
2400        /// [`test_error_on_verification_problem_strategy_should_share_with_verified_device_of_pin_violation_user`]
2401        /// and [`test_identity_based_strategy_should_share_with_verified_device_of_pin_violation_user`].
2402        async fn should_share_with_verified_device_of_pin_violation_user(
2403            encryption_settings: &EncryptionSettings,
2404        ) {
2405            let machine = test_machine().await;
2406
2407            // Bob starts out with one identity
2408            let bob_user_id = user_id!("@bob:localhost");
2409            let keys_query =
2410                key_query_response_template_with_cross_signing(bob_user_id).build_response();
2411            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2412
2413            // He then changes identity, and adds a dehydrated device (signed with his new
2414            // identity)
2415            let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
2416            let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
2417                .with_dehydrated_device(bob_dehydrated_device_id, true)
2418                .build_response();
2419            allow_duplicates! {
2420                with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2421                    assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
2422                });
2423            }
2424            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2425
2426            // When we collect the recipients ...
2427            let recips = share_test_session_and_collect_recipients(
2428                &machine,
2429                bob_user_id,
2430                encryption_settings,
2431            )
2432            .await;
2433
2434            // ... then the dehydrated device should be included
2435            assert_shared_with(recips, bob_user_id, [bob_dehydrated_device_id].into());
2436        }
2437
2438        #[async_test]
2439        async fn test_all_devices_strategy_should_not_share_with_dehydrated_device_of_verification_violation_user()
2440         {
2441            should_not_share_with_dehydrated_device_of_verification_violation_user(
2442                &all_devices_strategy_settings(),
2443            )
2444            .await
2445        }
2446
2447        /// Helper function for
2448        /// [`test_all_devices_strategy_should_not_share_with_dehydrated_device_of_verification_violation_user`].
2449        async fn should_not_share_with_dehydrated_device_of_verification_violation_user(
2450            encryption_settings: &EncryptionSettings,
2451        ) {
2452            let bob_user_id = user_id!("@bob:localhost");
2453            let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
2454            let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
2455                bob_user_id,
2456                bob_dehydrated_device_id,
2457            )
2458            .await;
2459
2460            // When we collect the recipients ...
2461            let recips = share_test_session_and_collect_recipients(
2462                &machine,
2463                bob_user_id,
2464                encryption_settings,
2465            )
2466            .await;
2467
2468            // ... it shouldn't be shared with anyone, and there should be a withheld
2469            // message for the dehydrated device.
2470            assert_withheld_to(recips, bob_user_id, bob_dehydrated_device_id);
2471        }
2472
2473        #[async_test]
2474        async fn test_error_on_verification_problem_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user()
2475         {
2476            should_give_error_for_dehydrated_device_of_verification_violation_user(
2477                &error_on_verification_problem_encryption_settings(),
2478            )
2479            .await
2480        }
2481
2482        #[async_test]
2483        async fn test_identity_based_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user()
2484         {
2485            // This hits the same codepath as
2486            // `test_share_identity_strategy_report_verification_violation`, but
2487            // we test dehydrated devices here specifically, for completeness.
2488            should_give_error_for_dehydrated_device_of_verification_violation_user(
2489                &identity_based_strategy_settings(),
2490            )
2491            .await
2492        }
2493
2494        /// Common helper for
2495        /// [`test_error_on_verification_problem_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user`]
2496        /// and [`test_identity_based_strategy_should_give_error_for_dehydrated_device_of_verification_violation_user`].
2497        async fn should_give_error_for_dehydrated_device_of_verification_violation_user(
2498            encryption_settings: &EncryptionSettings,
2499        ) {
2500            let bob_user_id = user_id!("@bob:localhost");
2501            let bob_dehydrated_device_id = device_id!("DEHYDRATED_DEVICE");
2502            let machine = prepare_machine_with_dehydrated_device_of_verification_violation_user(
2503                bob_user_id,
2504                bob_dehydrated_device_id,
2505            )
2506            .await;
2507
2508            let group_session = create_test_outbound_group_session(&machine, encryption_settings);
2509            let share_result = collect_session_recipients(
2510                machine.store(),
2511                iter::once(bob_user_id),
2512                encryption_settings,
2513                &group_session,
2514            )
2515            .await;
2516
2517            // The key share should fail with an error indicating that recipients
2518            // were previously verified.
2519            assert_matches::assert_matches!(
2520                share_result,
2521                Err(crate::OlmError::SessionRecipientCollectionError(
2522                    crate::SessionRecipientCollectionError::VerifiedUserChangedIdentity(_)
2523                ))
2524            );
2525        }
2526
2527        /// Prepare an OlmMachine which knows about a user `bob_user_id`, who
2528        /// has recently changed identity, and then added a new
2529        /// dehydrated device `bob_dehydrated_device_id`.
2530        async fn prepare_machine_with_dehydrated_device_of_verification_violation_user(
2531            bob_user_id: &UserId,
2532            bob_dehydrated_device_id: &DeviceId,
2533        ) -> OlmMachine {
2534            let machine = test_machine().await;
2535
2536            // Bob starts out with one identity, which we have verified
2537            let keys_query = key_query_response_template_with_cross_signing(bob_user_id)
2538                .with_user_verification_signature(
2539                    KeyDistributionTestData::me_id(),
2540                    &KeyDistributionTestData::me_private_user_signing_key(),
2541                )
2542                .build_response();
2543            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2544
2545            // He then changes identity, and adds a dehydrated device (signed with his new
2546            // identity)
2547            let keys_query = key_query_response_template_with_changed_cross_signing(bob_user_id)
2548                .with_dehydrated_device(bob_dehydrated_device_id, true)
2549                .build_response();
2550            allow_duplicates! {
2551                with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2552                    assert_json_snapshot!(ruma_response_to_json(keys_query.clone()))
2553                });
2554            }
2555            machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2556
2557            machine
2558        }
2559
2560        /// Create a test megolm session and prepare to share it with the given
2561        /// users, using the given sharing strategy.
2562        async fn share_test_session_and_collect_recipients(
2563            machine: &OlmMachine,
2564            target_user_id: &UserId,
2565            encryption_settings: &EncryptionSettings,
2566        ) -> CollectRecipientsResult {
2567            let group_session = create_test_outbound_group_session(machine, encryption_settings);
2568            collect_session_recipients(
2569                machine.store(),
2570                iter::once(target_user_id),
2571                encryption_settings,
2572                &group_session,
2573            )
2574            .await
2575            .unwrap()
2576        }
2577
2578        /// Assert that the session is shared with the given devices, and that
2579        /// there are no "withheld" messages
2580        fn assert_shared_with(
2581            recips: CollectRecipientsResult,
2582            user_id: &UserId,
2583            device_ids: HashSet<&DeviceId>,
2584        ) {
2585            let bob_devices_shared: HashSet<_> = recips
2586                .devices
2587                .get(user_id)
2588                .unwrap_or_else(|| panic!("session not shared with {user_id}"))
2589                .iter()
2590                .map(|d| d.device_id())
2591                .collect();
2592            assert_eq!(bob_devices_shared, device_ids);
2593
2594            assert!(recips.withheld_devices.is_empty(), "Unexpected withheld messages");
2595        }
2596
2597        /// Assert that the session is not shared with any devices, and that
2598        /// there is a withheld code for the given device.
2599        fn assert_withheld_to(
2600            recips: CollectRecipientsResult,
2601            bob_user_id: &UserId,
2602            bob_dehydrated_device_id: &DeviceId,
2603        ) {
2604            // The share list should be empty
2605            for (user, device_list) in recips.devices {
2606                assert_eq!(device_list.len(), 0, "session unexpectedly shared with {user}");
2607            }
2608
2609            // ... and there should be one withheld message
2610            assert_eq!(recips.withheld_devices.len(), 1);
2611            assert_eq!(recips.withheld_devices[0].0.user_id(), bob_user_id);
2612            assert_eq!(recips.withheld_devices[0].0.device_id(), bob_dehydrated_device_id);
2613            assert_eq!(recips.withheld_devices[0].1, WithheldCode::Unverified);
2614        }
2615
2616        /// Start a [`KeysQueryResponseTemplate`] for the given user, with
2617        /// cross-signing keys.
2618        fn key_query_response_template_with_cross_signing(
2619            user_id: &UserId,
2620        ) -> KeyQueryResponseTemplate {
2621            KeyQueryResponseTemplate::new(user_id.to_owned()).with_cross_signing_keys(
2622                Ed25519SecretKey::from_slice(b"master12master12master12master12"),
2623                Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
2624                Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
2625            )
2626        }
2627
2628        /// Start a [`KeysQueryResponseTemplate`] for the given user, with
2629        /// *different* cross signing key to
2630        /// [`key_query_response_template_with_cross_signing`].
2631        fn key_query_response_template_with_changed_cross_signing(
2632            bob_user_id: &UserId,
2633        ) -> KeyQueryResponseTemplate {
2634            KeyQueryResponseTemplate::new(bob_user_id.to_owned()).with_cross_signing_keys(
2635                Ed25519SecretKey::from_slice(b"newmaster__newmaster__newmaster_"),
2636                Ed25519SecretKey::from_slice(b"self1234self1234self1234self1234"),
2637                Ed25519SecretKey::from_slice(b"user1234user1234user1234user1234"),
2638            )
2639        }
2640
2641        trait KeyQueryResponseTemplateExt {
2642            fn with_dehydrated_device(
2643                self,
2644                device_id: &DeviceId,
2645                verified: bool,
2646            ) -> KeyQueryResponseTemplate;
2647        }
2648
2649        impl KeyQueryResponseTemplateExt for KeyQueryResponseTemplate {
2650            /// Add a dehydrated device to the KeyQueryResponseTemplate
2651            fn with_dehydrated_device(
2652                self,
2653                device_id: &DeviceId,
2654                verified: bool,
2655            ) -> KeyQueryResponseTemplate {
2656                self.with_device(
2657                    device_id,
2658                    &Curve25519PublicKey::from(b"curvepubcurvepubcurvepubcurvepub".to_owned()),
2659                    &Ed25519SecretKey::from_slice(b"device12device12device12device12"),
2660                    KeyQueryResponseTemplateDeviceOptions::new()
2661                        .dehydrated(true)
2662                        .verified(verified),
2663                )
2664            }
2665        }
2666    }
2667
2668    #[async_test]
2669    async fn test_share_with_identity_strategy() {
2670        let machine = test_machine().await;
2671        import_known_users_to_test_machine(&machine).await;
2672
2673        let encryption_settings = identity_based_strategy_settings();
2674
2675        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2676
2677        let share_result = collect_session_recipients(
2678            machine.store(),
2679            vec![
2680                KeyDistributionTestData::dan_id(),
2681                KeyDistributionTestData::dave_id(),
2682                KeyDistributionTestData::good_id(),
2683            ]
2684            .into_iter(),
2685            &encryption_settings,
2686            &group_session,
2687        )
2688        .await
2689        .unwrap();
2690
2691        assert!(!share_result.should_rotate);
2692
2693        let dave_devices_shared = share_result.devices.get(KeyDistributionTestData::dave_id());
2694        let good_devices_shared = share_result.devices.get(KeyDistributionTestData::good_id());
2695        // dave has no published identity so will not receive the key
2696        assert!(dave_devices_shared.unwrap().is_empty());
2697
2698        // @good has properly signed his devices, he should get the keys
2699        assert_eq!(good_devices_shared.unwrap().len(), 2);
2700
2701        // dan has one of his devices self signed, so should get
2702        // the key
2703        let dan_devices_shared =
2704            share_result.devices.get(KeyDistributionTestData::dan_id()).unwrap();
2705
2706        assert_eq!(dan_devices_shared.len(), 1);
2707        let dan_device_that_will_get_the_key = &dan_devices_shared[0];
2708        assert_eq!(
2709            dan_device_that_will_get_the_key.device_id().as_str(),
2710            KeyDistributionTestData::dan_signed_device_id()
2711        );
2712
2713        // Check withhelds for others
2714        let (_, code) = share_result
2715            .withheld_devices
2716            .iter()
2717            .find(|(d, _)| d.device_id() == KeyDistributionTestData::dan_unsigned_device_id())
2718            .expect("This dan's device should receive a withheld code");
2719
2720        assert_eq!(code, &WithheldCode::Unverified);
2721
2722        // Check withhelds for others
2723        let (_, code) = share_result
2724            .withheld_devices
2725            .iter()
2726            .find(|(d, _)| d.device_id() == KeyDistributionTestData::dave_device_id())
2727            .expect("This dave device should receive a withheld code");
2728
2729        assert_eq!(code, &WithheldCode::Unverified);
2730    }
2731
2732    /// Test key sharing with the identity-based strategy with different
2733    /// states of our own verification.
2734    #[async_test]
2735    async fn test_share_identity_strategy_no_cross_signing() {
2736        // Starting off, we have not yet set up our own cross-signing, so
2737        // sharing with the identity-based strategy should fail.
2738        let machine: OlmMachine = OlmMachine::new(
2739            KeyDistributionTestData::me_id(),
2740            KeyDistributionTestData::me_device_id(),
2741        )
2742        .await;
2743
2744        let keys_query = KeyDistributionTestData::dan_keys_query_response();
2745        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2746
2747        let fake_room_id = room_id!("!roomid:localhost");
2748
2749        let encryption_settings = identity_based_strategy_settings();
2750
2751        let request_result = machine
2752            .share_room_key(
2753                fake_room_id,
2754                iter::once(KeyDistributionTestData::dan_id()),
2755                encryption_settings.clone(),
2756            )
2757            .await;
2758
2759        assert_matches!(
2760            request_result,
2761            Err(OlmError::SessionRecipientCollectionError(
2762                SessionRecipientCollectionError::CrossSigningNotSetup
2763            ))
2764        );
2765
2766        // We now get our public cross-signing keys, but we don't trust them
2767        // yet.  In this case, sharing the keys should still fail since our own
2768        // device is still unverified.
2769        let keys_query = KeyDistributionTestData::me_keys_query_response();
2770        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2771
2772        let request_result = machine
2773            .share_room_key(
2774                fake_room_id,
2775                iter::once(KeyDistributionTestData::dan_id()),
2776                encryption_settings.clone(),
2777            )
2778            .await;
2779
2780        assert_matches!(
2781            request_result,
2782            Err(OlmError::SessionRecipientCollectionError(
2783                SessionRecipientCollectionError::SendingFromUnverifiedDevice
2784            ))
2785        );
2786
2787        // Finally, after we trust our own cross-signing keys, key sharing
2788        // should succeed.
2789        machine
2790            .import_cross_signing_keys(CrossSigningKeyExport {
2791                master_key: KeyDistributionTestData::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
2792                self_signing_key: KeyDistributionTestData::SELF_SIGNING_KEY_PRIVATE_EXPORT
2793                    .to_owned()
2794                    .into(),
2795                user_signing_key: KeyDistributionTestData::USER_SIGNING_KEY_PRIVATE_EXPORT
2796                    .to_owned()
2797                    .into(),
2798            })
2799            .await
2800            .unwrap();
2801
2802        let requests = machine
2803            .share_room_key(
2804                fake_room_id,
2805                iter::once(KeyDistributionTestData::dan_id()),
2806                encryption_settings.clone(),
2807            )
2808            .await
2809            .unwrap();
2810
2811        // Dan has two devices, but only one is cross-signed, so there should
2812        // only be one key share.
2813        assert_eq!(requests.len(), 1);
2814    }
2815
2816    /// Test that identity-based key sharing gives an error when a verified
2817    /// user changes their identity, and that the key can be shared when the
2818    /// identity change is resolved.
2819    #[async_test]
2820    async fn test_share_identity_strategy_report_verification_violation() {
2821        let machine: OlmMachine = OlmMachine::new(
2822            KeyDistributionTestData::me_id(),
2823            KeyDistributionTestData::me_device_id(),
2824        )
2825        .await;
2826
2827        machine.bootstrap_cross_signing(false).await.unwrap();
2828
2829        // We will try sending a key to two different users.
2830        let user1 = IdentityChangeDataSet::user_id();
2831        let user2 = MaloIdentityChangeDataSet::user_id();
2832
2833        // We first get both users' initial device and identity keys.
2834        let keys_query = IdentityChangeDataSet::key_query_with_identity_a();
2835        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2836
2837        let keys_query = MaloIdentityChangeDataSet::initial_key_query();
2838        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2839
2840        // And then we get both user' changed identity keys.  We simulate a
2841        // verification violation by marking both users as having been
2842        // previously verified, in which case the key sharing should fail.
2843        let keys_query = IdentityChangeDataSet::key_query_with_identity_b();
2844        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2845        machine
2846            .get_identity(user1, None)
2847            .await
2848            .unwrap()
2849            .unwrap()
2850            .other()
2851            .unwrap()
2852            .mark_as_previously_verified()
2853            .await
2854            .unwrap();
2855
2856        let keys_query = MaloIdentityChangeDataSet::updated_key_query();
2857        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
2858        machine
2859            .get_identity(user2, None)
2860            .await
2861            .unwrap()
2862            .unwrap()
2863            .other()
2864            .unwrap()
2865            .mark_as_previously_verified()
2866            .await
2867            .unwrap();
2868
2869        let fake_room_id = room_id!("!roomid:localhost");
2870
2871        // We share the key using the identity-based strategy.
2872        let encryption_settings = identity_based_strategy_settings();
2873
2874        let request_result = machine
2875            .share_room_key(
2876                fake_room_id,
2877                vec![user1, user2].into_iter(),
2878                encryption_settings.clone(),
2879            )
2880            .await;
2881
2882        // The key share should fail with an error indicating that recipients
2883        // were previously verified.
2884        assert_let!(
2885            Err(OlmError::SessionRecipientCollectionError(
2886                SessionRecipientCollectionError::VerifiedUserChangedIdentity(affected_users)
2887            )) = request_result
2888        );
2889        // Both our recipients should be in `affected_users`.
2890        assert_eq!(2, affected_users.len());
2891
2892        // We resolve this for user1 by withdrawing their verification.
2893        machine
2894            .get_identity(user1, None)
2895            .await
2896            .unwrap()
2897            .unwrap()
2898            .withdraw_verification()
2899            .await
2900            .unwrap();
2901
2902        // We resolve this for user2 by re-verifying.
2903        let verification_request = machine
2904            .get_identity(user2, None)
2905            .await
2906            .unwrap()
2907            .unwrap()
2908            .other()
2909            .unwrap()
2910            .verify()
2911            .await
2912            .unwrap();
2913
2914        let master_key =
2915            &machine.get_identity(user2, None).await.unwrap().unwrap().other().unwrap().master_key;
2916
2917        let my_identity = machine
2918            .get_identity(KeyDistributionTestData::me_id(), None)
2919            .await
2920            .expect("Should not fail to find own identity")
2921            .expect("Our own identity should not be missing")
2922            .own()
2923            .expect("Our own identity should be of type Own");
2924
2925        let msk = json!({ user2: serde_json::to_value(master_key).expect("Should not fail to serialize")});
2926        let ssk =
2927            serde_json::to_value(&MaloIdentityChangeDataSet::updated_key_query().self_signing_keys)
2928                .expect("Should not fail to serialize");
2929
2930        let kq_response = simulate_key_query_response_for_verification(
2931            verification_request,
2932            my_identity,
2933            KeyDistributionTestData::me_id(),
2934            user2,
2935            msk,
2936            ssk,
2937        );
2938
2939        machine
2940            .mark_request_as_sent(
2941                &TransactionId::new(),
2942                crate::types::requests::AnyIncomingResponse::KeysQuery(&kq_response),
2943            )
2944            .await
2945            .unwrap();
2946
2947        assert!(machine.get_identity(user2, None).await.unwrap().unwrap().is_verified());
2948
2949        // And now the key share should succeed.
2950        machine
2951            .share_room_key(
2952                fake_room_id,
2953                vec![user1, user2].into_iter(),
2954                encryption_settings.clone(),
2955            )
2956            .await
2957            .unwrap();
2958    }
2959
2960    #[async_test]
2961    async fn test_should_rotate_based_on_visibility() {
2962        let machine = test_machine().await;
2963        import_known_users_to_test_machine(&machine).await;
2964
2965        let strategy = CollectStrategy::AllDevices;
2966
2967        let encryption_settings = EncryptionSettings {
2968            sharing_strategy: strategy.clone(),
2969            history_visibility: HistoryVisibility::Invited,
2970            ..Default::default()
2971        };
2972
2973        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
2974
2975        let _ = collect_session_recipients(
2976            machine.store(),
2977            vec![KeyDistributionTestData::dan_id()].into_iter(),
2978            &encryption_settings,
2979            &group_session,
2980        )
2981        .await
2982        .unwrap();
2983
2984        // Try to share again with updated history visibility
2985        let encryption_settings = EncryptionSettings {
2986            sharing_strategy: strategy.clone(),
2987            history_visibility: HistoryVisibility::Shared,
2988            ..Default::default()
2989        };
2990
2991        let share_result = collect_session_recipients(
2992            machine.store(),
2993            vec![KeyDistributionTestData::dan_id()].into_iter(),
2994            &encryption_settings,
2995            &group_session,
2996        )
2997        .await
2998        .unwrap();
2999
3000        assert!(share_result.should_rotate);
3001    }
3002
3003    /// Test that the session is rotated when a device is removed from the
3004    /// recipients. In that case we simulate that dan has logged out one of
3005    /// his devices.
3006    #[async_test]
3007    async fn test_should_rotate_based_on_device_excluded() {
3008        let machine = test_machine().await;
3009        import_known_users_to_test_machine(&machine).await;
3010
3011        let encryption_settings = all_devices_strategy_settings();
3012        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
3013        let sender_key = machine.identity_keys().curve25519;
3014
3015        group_session
3016            .mark_shared_with(
3017                KeyDistributionTestData::dan_id(),
3018                KeyDistributionTestData::dan_signed_device_id(),
3019                sender_key,
3020            )
3021            .await;
3022        group_session
3023            .mark_shared_with(
3024                KeyDistributionTestData::dan_id(),
3025                KeyDistributionTestData::dan_unsigned_device_id(),
3026                sender_key,
3027            )
3028            .await;
3029
3030        // Try to share again after dan has removed one of his devices
3031        let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
3032        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
3033
3034        // share again
3035        let share_result = collect_session_recipients(
3036            machine.store(),
3037            vec![KeyDistributionTestData::dan_id()].into_iter(),
3038            &encryption_settings,
3039            &group_session,
3040        )
3041        .await
3042        .unwrap();
3043
3044        assert!(share_result.should_rotate);
3045    }
3046
3047    /// Test that the session is rotated if a devices has a pending
3048    /// to-device request that would share the keys with it.
3049    #[async_test]
3050    async fn test_should_rotate_based_on_device_with_pending_request_excluded() {
3051        let machine = test_machine().await;
3052        import_known_users_to_test_machine(&machine).await;
3053
3054        let encryption_settings = all_devices_strategy_settings();
3055        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
3056        let sender_key = machine.identity_keys().curve25519;
3057
3058        let dan_user = KeyDistributionTestData::dan_id();
3059        let dan_dev1 = KeyDistributionTestData::dan_signed_device_id();
3060        let dan_dev2 = KeyDistributionTestData::dan_unsigned_device_id();
3061
3062        // Share the session with device 1
3063        group_session.mark_shared_with(dan_user, dan_dev1, sender_key).await;
3064
3065        {
3066            // Add a pending request to share with device 2
3067            let share_infos = BTreeMap::from([(
3068                dan_user.to_owned(),
3069                BTreeMap::from([(
3070                    dan_dev2.to_owned(),
3071                    ShareInfo::new_shared(sender_key, 0, SequenceNumber::default()),
3072                )]),
3073            )]);
3074
3075            let txid = TransactionId::new();
3076            let req = Arc::new(ToDeviceRequest::for_recipients(
3077                dan_user,
3078                vec![dan_dev2.to_owned()],
3079                &ruma::events::AnyToDeviceEventContent::Dummy(ToDeviceDummyEventContent),
3080                txid.clone(),
3081            ));
3082            group_session.add_request(txid, req, share_infos);
3083        }
3084
3085        // Remove device 2
3086        let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
3087        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
3088
3089        // Share again
3090        let share_result = collect_session_recipients(
3091            machine.store(),
3092            vec![KeyDistributionTestData::dan_id()].into_iter(),
3093            &encryption_settings,
3094            &group_session,
3095        )
3096        .await
3097        .unwrap();
3098
3099        assert!(share_result.should_rotate);
3100    }
3101
3102    /// Test that the session is not rotated if a devices is removed
3103    /// but was already withheld from receiving the session.
3104    #[async_test]
3105    async fn test_should_not_rotate_if_keys_were_withheld() {
3106        let machine = test_machine().await;
3107        import_known_users_to_test_machine(&machine).await;
3108
3109        let encryption_settings = all_devices_strategy_settings();
3110        let group_session = create_test_outbound_group_session(&machine, &encryption_settings);
3111        let fake_room_id = group_session.room_id();
3112
3113        // Because we don't have Olm sessions initialized, this will contain
3114        // withheld requests for both of Dan's devices
3115        let requests = machine
3116            .share_room_key(
3117                fake_room_id,
3118                vec![KeyDistributionTestData::dan_id()].into_iter(),
3119                encryption_settings.clone(),
3120            )
3121            .await
3122            .unwrap();
3123
3124        for r in requests {
3125            machine
3126                .inner
3127                .group_session_manager
3128                .mark_request_as_sent(r.as_ref().txn_id.as_ref())
3129                .await
3130                .unwrap();
3131        }
3132
3133        // Try to share again after dan has removed one of his devices
3134        let keys_query = KeyDistributionTestData::dan_keys_query_response_device_loggedout();
3135        machine.mark_request_as_sent(&TransactionId::new(), &keys_query).await.unwrap();
3136
3137        // share again
3138        let share_result = collect_session_recipients(
3139            machine.store(),
3140            vec![KeyDistributionTestData::dan_id()].into_iter(),
3141            &encryption_settings,
3142            &group_session,
3143        )
3144        .await
3145        .unwrap();
3146
3147        assert!(!share_result.should_rotate);
3148    }
3149
3150    /// Common setup for tests which require a verified user to have unsigned
3151    /// devices.
3152    ///
3153    /// Returns an `OlmMachine` which is properly configured with trusted
3154    /// cross-signing keys. Also imports a set of keys for
3155    /// Bob ([`VerificationViolationTestData::bob_id`]), where Bob is verified
3156    /// and has 2 devices, one signed and the other not.
3157    async fn unsigned_of_verified_setup() -> OlmMachine {
3158        use test_json::keys_query_sets::VerificationViolationTestData as DataSet;
3159
3160        let machine = OlmMachine::new(DataSet::own_id(), device_id!("LOCAL")).await;
3161
3162        // Tell the OlmMachine about our own public keys.
3163        let own_keys = DataSet::own_keys_query_response_1();
3164        machine.mark_request_as_sent(&TransactionId::new(), &own_keys).await.unwrap();
3165
3166        // Import the secret parts of our own cross-signing keys.
3167        machine
3168            .import_cross_signing_keys(CrossSigningKeyExport {
3169                master_key: DataSet::MASTER_KEY_PRIVATE_EXPORT.to_owned().into(),
3170                self_signing_key: DataSet::SELF_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
3171                user_signing_key: DataSet::USER_SIGNING_KEY_PRIVATE_EXPORT.to_owned().into(),
3172            })
3173            .await
3174            .unwrap();
3175
3176        // Tell the OlmMachine about Bob's keys.
3177        let bob_keys = DataSet::bob_keys_query_response_signed();
3178        machine.mark_request_as_sent(&TransactionId::new(), &bob_keys).await.unwrap();
3179
3180        // Double-check the state of Bob: he should be verified, and should have one
3181        // signed and one unsigned device.
3182        let bob_identity = machine.get_identity(DataSet::bob_id(), None).await.unwrap().unwrap();
3183        assert!(bob_identity.other().unwrap().is_verified());
3184
3185        let bob_signed_device = machine
3186            .get_device(DataSet::bob_id(), DataSet::bob_device_1_id(), None)
3187            .await
3188            .unwrap()
3189            .unwrap();
3190        assert!(bob_signed_device.is_verified());
3191        assert!(bob_signed_device.device_owner_identity.is_some());
3192
3193        let bob_unsigned_device = machine
3194            .get_device(DataSet::bob_id(), DataSet::bob_device_2_id(), None)
3195            .await
3196            .unwrap()
3197            .unwrap();
3198        assert!(!bob_unsigned_device.is_verified());
3199
3200        machine
3201    }
3202
3203    /// [`EncryptionSettings`] with [`CollectStrategy::AllDevices`]
3204    fn all_devices_strategy_settings() -> EncryptionSettings {
3205        EncryptionSettings { sharing_strategy: CollectStrategy::AllDevices, ..Default::default() }
3206    }
3207
3208    /// [`EncryptionSettings`] with
3209    /// [`CollectStrategy::ErrorOnVerifiedUserProblem`]
3210    fn error_on_verification_problem_encryption_settings() -> EncryptionSettings {
3211        EncryptionSettings {
3212            sharing_strategy: CollectStrategy::ErrorOnVerifiedUserProblem,
3213            ..Default::default()
3214        }
3215    }
3216
3217    /// [`EncryptionSettings`] with [`CollectStrategy::IdentityBasedStrategy`]
3218    fn identity_based_strategy_settings() -> EncryptionSettings {
3219        EncryptionSettings {
3220            sharing_strategy: CollectStrategy::IdentityBasedStrategy,
3221            ..Default::default()
3222        }
3223    }
3224
3225    /// Create an [`OutboundGroupSession`], backed by the given olm machine,
3226    /// without sharing it.
3227    fn create_test_outbound_group_session(
3228        machine: &OlmMachine,
3229        encryption_settings: &EncryptionSettings,
3230    ) -> OutboundGroupSession {
3231        OutboundGroupSession::new(
3232            machine.device_id().into(),
3233            Arc::new(machine.identity_keys()),
3234            room_id!("!roomid:localhost"),
3235            encryption_settings.clone(),
3236        )
3237        .expect("creating an outbound group session should not fail")
3238    }
3239}