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