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