Skip to main content

matrix_sdk_crypto/session_manager/group_sessions/
share_strategy.rs

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