Skip to main content

matrix_sdk_crypto/machine/
mod.rs

1// Copyright 2020, 2026 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
15#[cfg(feature = "experimental-encrypted-state-events")]
16use std::borrow::Borrow;
17use std::{
18    collections::{BTreeMap, HashMap, HashSet},
19    sync::Arc,
20    time::Duration,
21};
22
23use itertools::Itertools;
24#[cfg(feature = "experimental-send-custom-to-device")]
25use matrix_sdk_common::deserialized_responses::WithheldCode;
26use matrix_sdk_common::{
27    BoxFuture,
28    deserialized_responses::{
29        AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ForwarderInfo,
30        ProcessedToDeviceEvent, ToDeviceUnableToDecryptInfo, ToDeviceUnableToDecryptReason,
31        UnableToDecryptInfo, UnableToDecryptReason, UnsignedDecryptionResult,
32        UnsignedEventLocation, VerificationLevel, VerificationState,
33    },
34    locks::RwLock as StdRwLock,
35    timer,
36};
37#[cfg(feature = "experimental-encrypted-state-events")]
38use ruma::events::{AnyStateEventContent, StateEventContent};
39use ruma::{
40    DeviceId, DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OneTimeKeyAlgorithm, OwnedDeviceId,
41    OwnedDeviceKeyId, OwnedTransactionId, OwnedUserId, RoomId, TransactionId, UInt, UserId,
42    api::client::{
43        dehydrated_device::DehydratedDeviceData,
44        keys::{
45            claim_keys::v3::Request as KeysClaimRequest,
46            get_keys::v3::Response as KeysQueryResponse,
47            upload_keys::v3::{Request as UploadKeysRequest, Response as UploadKeysResponse},
48            upload_signatures::v3::Request as UploadSignaturesRequest,
49        },
50        sync::sync_events::DeviceLists,
51    },
52    assign,
53    events::{
54        AnyMessageLikeEvent, AnyMessageLikeEventContent, AnyTimelineEvent, AnyToDeviceEvent,
55        MessageLikeEventContent, secret::request::SecretName,
56    },
57    serde::{JsonObject, Raw},
58};
59use serde::Serialize;
60use serde_json::{Value, value::to_raw_value};
61use tokio::sync::Mutex;
62use tracing::{
63    Span, debug, enabled, error,
64    field::{debug, display},
65    info, instrument, trace, warn,
66};
67use vodozemac::{Curve25519PublicKey, Ed25519Signature, megolm::DecryptionError};
68
69#[cfg(feature = "experimental-push-secrets")]
70use crate::error::SecretPushError;
71#[cfg(feature = "experimental-send-custom-to-device")]
72use crate::session_manager::split_devices_for_share_strategy;
73use crate::{
74    CollectStrategy, CryptoStoreError, DecryptionSettings, DeviceData, LocalTrust,
75    RoomEventDecryptionResult, SignatureError, TrustRequirement,
76    backups::{BackupMachine, MegolmV1BackupKey},
77    dehydrated_devices::{DehydratedDevices, DehydrationError},
78    error::{EventError, MegolmError, MegolmResult, OlmError, OlmResult, SetRoomSettingsError},
79    gossiping::GossipMachine,
80    identities::{Device, IdentityManager, UserDevices, user::UserIdentity},
81    olm::{
82        Account, CrossSigningStatus, EncryptionSettings, IdentityKeys, InboundGroupSession,
83        KnownSenderData, OlmDecryptionInfo, PrivateCrossSigningIdentity, SenderData,
84        SenderDataFinder, SessionType, StaticAccountData,
85    },
86    session_manager::{GroupSessionManager, SessionManager},
87    store::{
88        CryptoStoreWrapper, DynCryptoStore, IntoCryptoStore, MemoryStore, Result as StoreResult,
89        SecretImportError, Store, StoreTransaction,
90        caches::StoreCache,
91        types::{
92            Changes, CrossSigningKeyExport, DeviceChanges, IdentityChanges, PendingChanges,
93            RoomKeyInfo, RoomSettings, StoredRoomKeyBundleData,
94        },
95    },
96    types::{
97        EventEncryptionAlgorithm, Signatures,
98        events::{
99            ToDeviceEvent, ToDeviceEvents,
100            olm_v1::{AnyDecryptedOlmEvent, DecryptedRoomKeyBundleEvent, DecryptedRoomKeyEvent},
101            room::encrypted::{
102                EncryptedEvent, EncryptedToDeviceEvent, RoomEncryptedEventContent,
103                RoomEventEncryptionScheme, SupportedEventEncryptionSchemes,
104                ToDeviceEncryptedEventContent,
105            },
106            room_key::{MegolmV1AesSha2Content, RoomKeyContent},
107            room_key_bundle::RoomKeyBundleContent,
108            room_key_withheld::{
109                MegolmV1AesSha2WithheldContent, RoomKeyWithheldContent, RoomKeyWithheldEvent,
110            },
111        },
112        requests::{
113            AnyIncomingResponse, KeysQueryRequest, OutgoingRequest, ToDeviceRequest,
114            UploadSigningKeysRequest,
115        },
116    },
117    utilities::timestamp_to_iso8601,
118    verification::{Verification, VerificationMachine, VerificationRequest},
119};
120
121#[derive(Debug, Serialize)]
122/// The result of encrypting a room event.
123pub struct RawEncryptionResult {
124    /// The encrypted event content.
125    pub content: Raw<RoomEncryptedEventContent>,
126    /// Information about the encryption that was performed.
127    pub encryption_info: EncryptionInfo,
128}
129
130/// A builder object to help creating an [`OlmMachine`] instance.
131pub struct OlmMachineBuilder {
132    /// The unique id of the user that owns the machine to be built.
133    user_id: OwnedUserId,
134
135    /// The unique id of the device that owns the machine to be built.
136    device_id: OwnedDeviceId,
137
138    /// `CryptoStore` implementation. If not populated, a [`MemoryStore`] will
139    /// be created.
140    store: Option<Arc<DynCryptoStore>>,
141
142    /// Optional override for the vodozemac `Account` that will be used for this
143    /// OlmMachine.
144    custom_account: Option<vodozemac::olm::Account>,
145}
146
147impl OlmMachineBuilder {
148    /// Create a new `OlmMachineBuilder` which will create an [`OlmMachine`]
149    /// belonging to the given user id / device id.
150    pub fn new(user_id: &UserId, device_id: &DeviceId) -> Self {
151        Self {
152            user_id: user_id.to_owned(),
153            device_id: device_id.to_owned(),
154            store: None,
155            custom_account: None,
156        }
157    }
158
159    /// Set the [`CryptoStore`] implementation that will be used to store
160    /// the encryption keys.
161    ///
162    /// If this is not populated before [`OlmMachineBuilder::build`] is called,
163    /// a new [`MemoryStore`] will be created.
164    ///
165    /// [`CryptoStore`]: crate::store::CryptoStore
166    pub fn with_crypto_store(mut self, store: impl IntoCryptoStore) -> Self {
167        self.store = Some(store.into_crypto_store());
168        self
169    }
170
171    /// Set a custom [`vodozemac::olm::Account`] to be used for the identity and
172    /// one-time keys of this [`OlmMachine`]. If this is not set before
173    /// [`OlmMachineBuilder::build`] is called, a new default one or one
174    /// from the store will be used.
175    ///
176    /// If an account is provided and one already exists in the store for this
177    /// [`UserId`]/[`DeviceId`] combination, an error will be raised. This is
178    /// useful if one wishes to create identity keys before knowing the
179    /// user/device IDs, e.g., to use the identity key as the device ID.
180    pub fn with_custom_account(mut self, custom_account: Option<vodozemac::olm::Account>) -> Self {
181        self.custom_account = custom_account;
182        self
183    }
184
185    /// Construct a new [`OlmMachine`] from this builder.
186    ///
187    /// If a store was given via [`OlmMachineBuilder::with_crypto_store`], and
188    /// the store already contains encryption keys for the given user/device
189    /// pair, those will be re-used. Otherwise new device keys will be created
190    /// and stored.
191    pub async fn build(self) -> Result<OlmMachine, CryptoStoreError> {
192        OlmMachine::from_builder(self).await
193    }
194}
195
196impl std::fmt::Debug for OlmMachineBuilder {
197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        f.debug_struct("OlmMachineBuilder")
199            .field("user_id", &self.user_id)
200            .field("device_id", &self.device_id)
201            .finish_non_exhaustive()
202    }
203}
204
205/// State machine implementation of the Olm/Megolm encryption protocol used for
206/// Matrix end to end encryption.
207#[derive(Clone)]
208pub struct OlmMachine {
209    pub(crate) inner: Arc<OlmMachineInner>,
210}
211
212pub struct OlmMachineInner {
213    /// The unique user id that owns this account.
214    user_id: OwnedUserId,
215    /// The unique device ID of the device that holds this account.
216    device_id: OwnedDeviceId,
217    /// The private part of our cross signing identity.
218    /// Used to sign devices and other users, might be missing if some other
219    /// device bootstrapped cross signing or cross signing isn't bootstrapped at
220    /// all.
221    user_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
222    /// Store for the encryption keys.
223    /// Persists all the encryption keys so a client can resume the session
224    /// without the need to create new keys.
225    store: Store,
226    /// A state machine that handles Olm sessions creation.
227    session_manager: SessionManager,
228    /// A state machine that keeps track of our outbound group sessions.
229    pub(crate) group_session_manager: GroupSessionManager,
230    /// A state machine that is responsible to handle and keep track of SAS
231    /// verification flows.
232    verification_machine: VerificationMachine,
233    /// The state machine that is responsible to handle outgoing and incoming
234    /// key requests.
235    pub(crate) key_request_machine: GossipMachine,
236    /// State machine handling public user identities and devices, keeping track
237    /// of when a key query needs to be done and handling one.
238    identity_manager: IdentityManager,
239    /// A state machine that handles creating room key backups.
240    backup_machine: BackupMachine,
241}
242
243#[cfg(not(tarpaulin_include))]
244impl std::fmt::Debug for OlmMachine {
245    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246        f.debug_struct("OlmMachine")
247            .field("user_id", &self.user_id())
248            .field("device_id", &self.device_id())
249            .finish()
250    }
251}
252
253impl OlmMachine {
254    const CURRENT_GENERATION_STORE_KEY: &'static str = "generation-counter";
255    const HAS_MIGRATED_VERIFICATION_LATCH: &'static str = "HAS_MIGRATED_VERIFICATION_LATCH";
256
257    /// Create a new memory based OlmMachine.
258    ///
259    /// The created machine will keep the encryption keys only in memory and
260    /// once the object is dropped the keys will be lost.
261    ///
262    /// # Arguments
263    ///
264    /// * `user_id` - The unique id of the user that owns this machine.
265    ///
266    /// * `device_id` - The unique id of the device that owns this machine.
267    pub async fn new(user_id: &UserId, device_id: &DeviceId) -> Self {
268        OlmMachineBuilder::new(user_id, device_id)
269            .build()
270            .await
271            .expect("Reading and writing to the memory store always succeeds")
272    }
273
274    pub(crate) async fn rehydrate(
275        &self,
276        pickle_key: &[u8; 32],
277        device_id: &DeviceId,
278        device_data: Raw<DehydratedDeviceData>,
279    ) -> Result<OlmMachine, DehydrationError> {
280        let account = Account::rehydrate(pickle_key, self.user_id(), device_id, device_data)?;
281        let static_account = account.static_data().clone();
282
283        let store =
284            Arc::new(CryptoStoreWrapper::new(self.user_id(), device_id, MemoryStore::new()));
285        let device = DeviceData::from_account(&account);
286        store.save_pending_changes(PendingChanges { account: Some(account) }).await?;
287        store
288            .save_changes(Changes {
289                devices: DeviceChanges { new: vec![device], ..Default::default() },
290                ..Default::default()
291            })
292            .await?;
293
294        let (verification_machine, store, identity_manager) =
295            Self::new_helper_prelude(store, static_account, self.store().private_identity());
296
297        Ok(Self::new_helper(
298            device_id,
299            store,
300            verification_machine,
301            identity_manager,
302            self.store().private_identity(),
303            None,
304        ))
305    }
306
307    fn new_helper_prelude(
308        store_wrapper: Arc<CryptoStoreWrapper>,
309        account: StaticAccountData,
310        user_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
311    ) -> (VerificationMachine, Store, IdentityManager) {
312        let verification_machine =
313            VerificationMachine::new(account.clone(), user_identity.clone(), store_wrapper.clone());
314        let store = Store::new(account, user_identity, store_wrapper, verification_machine.clone());
315
316        let identity_manager = IdentityManager::new(store.clone());
317
318        (verification_machine, store, identity_manager)
319    }
320
321    fn new_helper(
322        device_id: &DeviceId,
323        store: Store,
324        verification_machine: VerificationMachine,
325        identity_manager: IdentityManager,
326        user_identity: Arc<Mutex<PrivateCrossSigningIdentity>>,
327        maybe_backup_key: Option<MegolmV1BackupKey>,
328    ) -> Self {
329        let group_session_manager = GroupSessionManager::new(store.clone());
330
331        let users_for_key_claim = Arc::new(StdRwLock::new(BTreeMap::new()));
332        let key_request_machine = GossipMachine::new(
333            store.clone(),
334            identity_manager.clone(),
335            group_session_manager.session_cache(),
336            users_for_key_claim.clone(),
337        );
338
339        let session_manager =
340            SessionManager::new(users_for_key_claim, key_request_machine.clone(), store.clone());
341
342        let backup_machine = BackupMachine::new(store.clone(), maybe_backup_key);
343
344        let inner = Arc::new(OlmMachineInner {
345            user_id: store.user_id().to_owned(),
346            device_id: device_id.to_owned(),
347            user_identity,
348            store,
349            session_manager,
350            group_session_manager,
351            verification_machine,
352            key_request_machine,
353            identity_manager,
354            backup_machine,
355        });
356
357        Self { inner }
358    }
359
360    #[instrument(skip(builder), fields(user_id, device_id, ed25519_key, curve25519_key))]
361    pub(crate) async fn from_builder(builder: OlmMachineBuilder) -> StoreResult<Self> {
362        let OlmMachineBuilder { user_id, device_id, store, custom_account } = builder;
363
364        let store = store.unwrap_or_else(|| MemoryStore::new().into_crypto_store());
365
366        let static_account = match store.load_account().await? {
367            Some(account) => {
368                if user_id != account.user_id()
369                    || device_id != account.device_id()
370                    || custom_account.is_some()
371                {
372                    return Err(CryptoStoreError::MismatchedAccount {
373                        expected: (account.user_id().to_owned(), account.device_id().to_owned()),
374                        got: (user_id.to_owned(), device_id.to_owned()),
375                    });
376                }
377
378                Span::current()
379                    .record("ed25519_key", display(account.identity_keys().ed25519))
380                    .record("curve25519_key", display(account.identity_keys().curve25519));
381                debug!("Restored an Olm account");
382
383                account.static_data().clone()
384            }
385
386            None => {
387                let account = if let Some(account) = custom_account {
388                    Account::new_helper(account, &user_id, &device_id)
389                } else {
390                    Account::with_device_id(&user_id, &device_id)
391                };
392
393                let static_account = account.static_data().clone();
394
395                Span::current()
396                    .record("ed25519_key", display(account.identity_keys().ed25519))
397                    .record("curve25519_key", display(account.identity_keys().curve25519));
398
399                let device = DeviceData::from_account(&account);
400
401                // We just created this device from our own Olm `Account`. Since we are the
402                // owners of the private keys of this device we can safely mark
403                // the device as verified.
404                device.set_trust_state(LocalTrust::Verified);
405
406                let changes = Changes {
407                    devices: DeviceChanges { new: vec![device], ..Default::default() },
408                    ..Default::default()
409                };
410                store.save_changes(changes).await?;
411                store.save_pending_changes(PendingChanges { account: Some(account) }).await?;
412
413                debug!("Created a new Olm account");
414
415                static_account
416            }
417        };
418
419        let identity = match store.load_identity().await? {
420            Some(i) => {
421                let master_key = i
422                    .master_public_key()
423                    .await
424                    .and_then(|m| m.get_first_key().map(|m| m.to_owned()));
425                debug!(?master_key, "Restored the cross signing identity");
426                i
427            }
428            None => {
429                debug!("Creating an empty cross signing identity stub");
430                PrivateCrossSigningIdentity::empty(&user_id)
431            }
432        };
433
434        // FIXME: This is a workaround for `regenerate_olm` clearing the backup
435        // state. Ideally, backups should not get automatically enabled since
436        // the `OlmMachine` doesn't get enough info from the homeserver for this
437        // to work reliably.
438        let saved_keys = store.load_backup_keys().await?;
439        let maybe_backup_key = saved_keys.decryption_key.and_then(|k| {
440            if let Some(version) = saved_keys.backup_version {
441                let megolm_v1_backup_key = k.megolm_v1_public_key();
442                megolm_v1_backup_key.set_version(version);
443                Some(megolm_v1_backup_key)
444            } else {
445                None
446            }
447        });
448
449        let identity = Arc::new(Mutex::new(identity));
450        let store = Arc::new(CryptoStoreWrapper::new(&user_id, &device_id, store));
451
452        let (verification_machine, store, identity_manager) =
453            Self::new_helper_prelude(store, static_account, identity.clone());
454
455        // FIXME: We might want in the future a more generic high-level data migration
456        // mechanism (at the store wrapper layer).
457        Self::migration_post_verified_latch_support(&store, &identity_manager).await?;
458
459        Ok(Self::new_helper(
460            &device_id,
461            store,
462            verification_machine,
463            identity_manager,
464            identity,
465            maybe_backup_key,
466        ))
467    }
468
469    // The sdk now support verified identity change detection.
470    // This introduces a new local flag (`verified_latch` on
471    // `OtherUserIdentityData`). In order to ensure that this flag is up-to-date and
472    // for the sake of simplicity we force a re-download of tracked users by marking
473    // them as dirty.
474    //
475    // pub(crate) visibility for testing.
476    pub(crate) async fn migration_post_verified_latch_support(
477        store: &Store,
478        identity_manager: &IdentityManager,
479    ) -> Result<(), CryptoStoreError> {
480        let maybe_migrate_for_identity_verified_latch =
481            store.get_custom_value(Self::HAS_MIGRATED_VERIFICATION_LATCH).await?.is_none();
482
483        if maybe_migrate_for_identity_verified_latch {
484            identity_manager.mark_all_tracked_users_as_dirty(store.cache().await?).await?;
485
486            store.set_custom_value(Self::HAS_MIGRATED_VERIFICATION_LATCH, vec![0]).await?
487        }
488        Ok(())
489    }
490
491    /// Get the crypto store associated with this `OlmMachine` instance.
492    pub fn store(&self) -> &Store {
493        &self.inner.store
494    }
495
496    /// The unique user id that owns this `OlmMachine` instance.
497    pub fn user_id(&self) -> &UserId {
498        &self.inner.user_id
499    }
500
501    /// The unique device ID that identifies this `OlmMachine`.
502    pub fn device_id(&self) -> &DeviceId {
503        &self.inner.device_id
504    }
505
506    /// The time at which the `Account` backing this `OlmMachine` was created.
507    ///
508    /// An [`Account`] is created when an `OlmMachine` is first instantiated
509    /// against a given [`Store`], at which point it creates identity keys etc.
510    /// This method returns the timestamp, according to the local clock, at
511    /// which that happened.
512    pub fn device_creation_time(&self) -> MilliSecondsSinceUnixEpoch {
513        self.inner.store.static_account().creation_local_time()
514    }
515
516    /// Get the public parts of our Olm identity keys.
517    pub fn identity_keys(&self) -> IdentityKeys {
518        let account = self.inner.store.static_account();
519        account.identity_keys()
520    }
521
522    /// Get the display name of our own device
523    pub async fn display_name(&self) -> StoreResult<Option<String>> {
524        self.store().device_display_name().await
525    }
526
527    /// Get the list of "tracked users".
528    ///
529    /// See [`update_tracked_users`](#method.update_tracked_users) for more
530    /// information.
531    pub async fn tracked_users(&self) -> StoreResult<HashSet<OwnedUserId>> {
532        let cache = self.store().cache().await?;
533        Ok(self.inner.identity_manager.key_query_manager.synced(&cache).await?.tracked_users())
534    }
535
536    /// Enable or disable room key requests.
537    ///
538    /// Room key requests allow the device to request room keys that it might
539    /// have missed in the original share using `m.room_key_request`
540    /// events.
541    ///
542    /// See also [`OlmMachine::set_room_key_forwarding_enabled`] and
543    /// [`OlmMachine::are_room_key_requests_enabled`].
544    #[cfg(feature = "automatic-room-key-forwarding")]
545    pub fn set_room_key_requests_enabled(&self, enable: bool) {
546        self.inner.key_request_machine.set_room_key_requests_enabled(enable)
547    }
548
549    /// Query whether we should send outgoing `m.room_key_request`s on
550    /// decryption failure.
551    ///
552    /// See also [`OlmMachine::set_room_key_requests_enabled`].
553    pub fn are_room_key_requests_enabled(&self) -> bool {
554        self.inner.key_request_machine.are_room_key_requests_enabled()
555    }
556
557    /// Enable or disable room key forwarding.
558    ///
559    /// If room key forwarding is enabled, we will automatically reply to
560    /// incoming `m.room_key_request` messages from verified devices by
561    /// forwarding the requested key (if we have it).
562    ///
563    /// See also [`OlmMachine::set_room_key_requests_enabled`] and
564    /// [`OlmMachine::is_room_key_forwarding_enabled`].
565    #[cfg(feature = "automatic-room-key-forwarding")]
566    pub fn set_room_key_forwarding_enabled(&self, enable: bool) {
567        self.inner.key_request_machine.set_room_key_forwarding_enabled(enable)
568    }
569
570    /// Is room key forwarding enabled?
571    ///
572    /// See also [`OlmMachine::set_room_key_forwarding_enabled`].
573    pub fn is_room_key_forwarding_enabled(&self) -> bool {
574        self.inner.key_request_machine.is_room_key_forwarding_enabled()
575    }
576
577    /// Get the outgoing requests that need to be sent out.
578    ///
579    /// This returns a list of [`OutgoingRequest`]. Those requests need to be
580    /// sent out to the server and the responses need to be passed back to
581    /// the state machine using [`mark_request_as_sent`].
582    ///
583    /// [`mark_request_as_sent`]: #method.mark_request_as_sent
584    pub async fn outgoing_requests(&self) -> StoreResult<Vec<OutgoingRequest>> {
585        let mut requests = Vec::new();
586
587        {
588            let store_cache = self.inner.store.cache().await?;
589            let account = store_cache.account().await?;
590            if let Some(r) = self.keys_for_upload(&account).await.map(|r| OutgoingRequest {
591                request_id: TransactionId::new(),
592                request: Arc::new(r.into()),
593            }) {
594                requests.push(r);
595            }
596        }
597
598        for request in self
599            .inner
600            .identity_manager
601            .users_for_key_query()
602            .await?
603            .into_iter()
604            .map(|(request_id, r)| OutgoingRequest { request_id, request: Arc::new(r.into()) })
605        {
606            requests.push(request);
607        }
608
609        requests.append(&mut self.inner.verification_machine.outgoing_messages());
610        requests.append(&mut self.inner.key_request_machine.outgoing_to_device_requests().await?);
611
612        Ok(requests)
613    }
614
615    /// Generate an "out-of-band" key query request for the given set of users.
616    ///
617    /// This can be useful if we need the results from [`get_identity`] or
618    /// [`get_user_devices`] to be as up-to-date as possible.
619    ///
620    /// Note that this request won't be awaited by other calls waiting for a
621    /// user's or device's keys, since this is an out-of-band query.
622    ///
623    /// # Arguments
624    ///
625    /// * `users` - list of users whose keys should be queried
626    ///
627    /// # Returns
628    ///
629    /// A request to be sent out to the server. Once sent, the response should
630    /// be passed back to the state machine using [`mark_request_as_sent`].
631    ///
632    /// [`mark_request_as_sent`]: OlmMachine::mark_request_as_sent
633    /// [`get_identity`]: OlmMachine::get_identity
634    /// [`get_user_devices`]: OlmMachine::get_user_devices
635    pub fn query_keys_for_users<'a>(
636        &self,
637        users: impl IntoIterator<Item = &'a UserId>,
638    ) -> (OwnedTransactionId, KeysQueryRequest) {
639        self.inner.identity_manager.build_key_query_for_users(users)
640    }
641
642    /// Mark the request with the given request id as sent.
643    ///
644    /// # Arguments
645    ///
646    /// * `request_id` - The unique id of the request that was sent out. This is
647    ///   needed to couple the response with the now sent out request.
648    ///
649    /// * `response` - The response that was received from the server after the
650    ///   outgoing request was sent out.
651    pub async fn mark_request_as_sent<'a>(
652        &self,
653        request_id: &TransactionId,
654        response: impl Into<AnyIncomingResponse<'a>>,
655    ) -> OlmResult<()> {
656        match response.into() {
657            AnyIncomingResponse::KeysUpload(response) => {
658                Box::pin(self.receive_keys_upload_response(response)).await?;
659            }
660            AnyIncomingResponse::KeysQuery(response) => {
661                Box::pin(self.receive_keys_query_response(request_id, response)).await?;
662            }
663            AnyIncomingResponse::KeysClaim(response) => {
664                Box::pin(
665                    self.inner.session_manager.receive_keys_claim_response(request_id, response),
666                )
667                .await?;
668            }
669            AnyIncomingResponse::ToDevice(_) => {
670                Box::pin(self.mark_to_device_request_as_sent(request_id)).await?;
671            }
672            AnyIncomingResponse::SigningKeysUpload(_) => {
673                Box::pin(self.receive_cross_signing_upload_response()).await?;
674            }
675            AnyIncomingResponse::SignatureUpload(_) => {
676                self.inner.verification_machine.mark_request_as_sent(request_id);
677            }
678            AnyIncomingResponse::RoomMessage(_) => {
679                self.inner.verification_machine.mark_request_as_sent(request_id);
680            }
681            AnyIncomingResponse::KeysBackup(_) => {
682                Box::pin(self.inner.backup_machine.mark_request_as_sent(request_id)).await?;
683            }
684        }
685
686        Ok(())
687    }
688
689    /// Mark the cross signing identity as shared.
690    async fn receive_cross_signing_upload_response(&self) -> StoreResult<()> {
691        let identity = self.inner.user_identity.lock().await;
692        identity.mark_as_shared();
693
694        let changes = Changes { private_identity: Some(identity.clone()), ..Default::default() };
695
696        self.store().save_changes(changes).await
697    }
698
699    /// Create a new cross signing identity and get the upload request to push
700    /// the new public keys to the server.
701    ///
702    /// **Warning**: if called with `reset`, this will delete any existing cross
703    /// signing keys that might exist on the server and thus will reset the
704    /// trust between all the devices.
705    ///
706    /// # Returns
707    ///
708    /// A triple of requests which should be sent out to the server, in the
709    /// order they appear in the return tuple.
710    ///
711    /// The first request's response, if present, should be passed back to the
712    /// state machine using [`mark_request_as_sent`].
713    ///
714    /// These requests may require user interactive auth.
715    ///
716    /// [`mark_request_as_sent`]: #method.mark_request_as_sent
717    pub async fn bootstrap_cross_signing(
718        &self,
719        reset: bool,
720    ) -> StoreResult<CrossSigningBootstrapRequests> {
721        // Don't hold the lock, otherwise we might deadlock in
722        // `bootstrap_cross_signing()` on `account` if a sync task is already
723        // running (which locks `account`), or we will deadlock
724        // in `upload_device_keys()` which locks private identity again.
725        let identity = self.inner.user_identity.lock().await.clone();
726
727        let (upload_signing_keys_req, upload_signatures_req) = if reset || identity.is_empty().await
728        {
729            info!("Creating new cross signing identity");
730
731            let (identity, upload_signing_keys_req, upload_signatures_req) = {
732                let cache = self.inner.store.cache().await?;
733                let account = cache.account().await?;
734                account.bootstrap_cross_signing().await
735            };
736
737            let public = identity.to_public_identity().await.expect(
738                "Couldn't create a public version of the identity from a new private identity",
739            );
740
741            *self.inner.user_identity.lock().await = identity.clone();
742
743            self.store()
744                .save_changes(Changes {
745                    identities: IdentityChanges { new: vec![public.into()], ..Default::default() },
746                    private_identity: Some(identity),
747                    ..Default::default()
748                })
749                .await?;
750
751            (upload_signing_keys_req, upload_signatures_req)
752        } else {
753            info!("Trying to upload the existing cross signing identity");
754            let upload_signing_keys_req = identity.as_upload_request().await;
755
756            // TODO remove this expect.
757            let upload_signatures_req = identity
758                .sign_account(self.inner.store.static_account())
759                .await
760                .expect("Can't sign device keys");
761
762            (upload_signing_keys_req, upload_signatures_req)
763        };
764
765        // If there are any *device* keys to upload (i.e. the account isn't shared),
766        // upload them before we upload the signatures, since the signatures may
767        // reference keys to be uploaded.
768        let upload_keys_req =
769            self.upload_device_keys().await?.map(|(_, request)| OutgoingRequest::from(request));
770
771        Ok(CrossSigningBootstrapRequests {
772            upload_signing_keys_req,
773            upload_keys_req,
774            upload_signatures_req,
775        })
776    }
777
778    /// Upload the device keys for this [`OlmMachine`].
779    ///
780    /// **Warning**: Do not use this method if
781    /// [`OlmMachine::outgoing_requests()`] is already in use. This method
782    /// is intended for explicitly uploading the device keys before starting
783    /// a sync and before using [`OlmMachine::outgoing_requests()`].
784    ///
785    /// # Returns
786    ///
787    /// A tuple containing a transaction ID and a request if the device keys
788    /// need to be uploaded. Otherwise, returns `None`.
789    pub async fn upload_device_keys(
790        &self,
791    ) -> StoreResult<Option<(OwnedTransactionId, UploadKeysRequest)>> {
792        let cache = self.store().cache().await?;
793        let account = cache.account().await?;
794
795        Ok(self.keys_for_upload(&account).await.map(|request| (TransactionId::new(), request)))
796    }
797
798    /// Receive a successful `/keys/upload` response.
799    ///
800    /// # Arguments
801    ///
802    /// * `response` - The response of the `/keys/upload` request that the
803    ///   client performed.
804    async fn receive_keys_upload_response(&self, response: &UploadKeysResponse) -> OlmResult<()> {
805        self.inner
806            .store
807            .with_transaction(async |tr| {
808                let account = tr.account().await?;
809                account.receive_keys_upload_response(response)?;
810                Ok(())
811            })
812            .await
813    }
814
815    /// Get a key claiming request for the user/device pairs that we are
816    /// missing Olm sessions for.
817    ///
818    /// Returns None if no key claiming request needs to be sent out.
819    ///
820    /// Sessions need to be established between devices so group sessions for a
821    /// room can be shared with them.
822    ///
823    /// This should be called every time a group session needs to be shared as
824    /// well as between sync calls. After a sync some devices may request room
825    /// keys without us having a valid Olm session with them, making it
826    /// impossible to server the room key request, thus it's necessary to check
827    /// for missing sessions between sync as well.
828    ///
829    /// **Note**: Care should be taken that only one such request at a time is
830    /// in flight, e.g. using a lock.
831    ///
832    /// The response of a successful key claiming requests needs to be passed to
833    /// the `OlmMachine` with the [`mark_request_as_sent`].
834    ///
835    /// # Arguments
836    ///
837    /// `users` - The list of users that we should check if we lack a session
838    /// with one of their devices. This can be an empty iterator when calling
839    /// this method between sync requests.
840    ///
841    /// [`mark_request_as_sent`]: #method.mark_request_as_sent
842    #[instrument(skip_all)]
843    pub async fn get_missing_sessions(
844        &self,
845        users: impl Iterator<Item = &UserId>,
846    ) -> StoreResult<Option<(OwnedTransactionId, KeysClaimRequest)>> {
847        self.inner.session_manager.get_missing_sessions(users).await
848    }
849
850    /// Receive a successful `/keys/query` response.
851    ///
852    /// Returns a list of newly discovered devices and devices that changed.
853    ///
854    /// # Arguments
855    ///
856    /// * `response` - The response of the `/keys/query` request that the client
857    ///   performed.
858    async fn receive_keys_query_response(
859        &self,
860        request_id: &TransactionId,
861        response: &KeysQueryResponse,
862    ) -> OlmResult<(DeviceChanges, IdentityChanges)> {
863        self.inner.identity_manager.receive_keys_query_response(request_id, response).await
864    }
865
866    /// Get a request to upload E2EE keys to the server.
867    ///
868    /// Returns None if no keys need to be uploaded.
869    ///
870    /// The response of a successful key upload requests needs to be passed to
871    /// the [`OlmMachine`] with the [`receive_keys_upload_response`].
872    ///
873    /// [`receive_keys_upload_response`]: #method.receive_keys_upload_response
874    async fn keys_for_upload(&self, account: &Account) -> Option<UploadKeysRequest> {
875        let (mut device_keys, one_time_keys, fallback_keys) = account.keys_for_upload();
876
877        // When uploading the device keys, if all private cross-signing keys are
878        // available locally, sign the device using these cross-signing keys.
879        // This will mark the device as verified if the user identity (i.e., the
880        // cross-signing keys) is also marked as verified.
881        //
882        // This approach eliminates the need to upload signatures in a separate request,
883        // ensuring that other users/devices will never encounter this device
884        // without a signature from their user identity. Consequently, they will
885        // never see the device as unverified.
886        if let Some(device_keys) = &mut device_keys {
887            let private_identity = self.store().private_identity();
888            let guard = private_identity.lock().await;
889
890            if guard.status().await.is_complete() {
891                guard.sign_device_keys(device_keys).await.expect(
892                    "We should be able to sign our device keys since we confirmed that we \
893                     have a complete set of private cross-signing keys",
894                );
895            }
896        }
897
898        if device_keys.is_none() && one_time_keys.is_empty() && fallback_keys.is_empty() {
899            None
900        } else {
901            let device_keys = device_keys.map(|d| d.to_raw());
902
903            Some(assign!(UploadKeysRequest::new(), {
904                device_keys, one_time_keys, fallback_keys
905            }))
906        }
907    }
908
909    /// Decrypt and handle a to-device event.
910    ///
911    /// If decryption (or checking the sender device) fails, returns an
912    /// `Err(DecryptToDeviceError::OlmError)`.
913    ///
914    /// If we are in strict "exclude insecure devices" mode and the sender
915    /// device is not verified, and the decrypted event type is not on the
916    /// allow list, returns `Err(DecryptToDeviceError::UnverifiedSender)`
917    ///
918    /// (The allow list of types that are processed even if the sender is
919    /// unverified is: `m.room_key`, `m.room_key.withheld`,
920    /// `m.room_key_request`, `m.secret.request` and `m.key.verification.*`.)
921    ///
922    /// If the sender device is dehydrated, does no handling and immediately
923    /// returns `Err(DecryptToDeviceError::FromDehydratedDevice)`.
924    ///
925    /// Otherwise, handles the decrypted event and returns it (decrypted) as
926    /// `Ok(OlmDecryptionInfo)`.
927    ///
928    /// # Arguments
929    ///
930    /// * `event` - The to-device event that should be decrypted.
931    async fn decrypt_to_device_event(
932        &self,
933        transaction: &mut StoreTransaction,
934        event: &EncryptedToDeviceEvent,
935        changes: &mut Changes,
936        decryption_settings: &DecryptionSettings,
937    ) -> Result<OlmDecryptionInfo, DecryptToDeviceError> {
938        // Decrypt the event
939        let mut decrypted = transaction
940            .account()
941            .await?
942            .decrypt_to_device_event(&self.inner.store, event, decryption_settings)
943            .await?;
944
945        // Return early if the sending device is a dehydrated device
946        self.check_to_device_event_is_not_from_dehydrated_device(&decrypted, &event.sender).await?;
947
948        // Device is not dehydrated: handle it as normal e.g. create a Megolm session
949        self.handle_decrypted_to_device_event(transaction.cache(), &mut decrypted, changes).await?;
950
951        Ok(decrypted)
952    }
953
954    #[instrument(
955        skip_all,
956        // This function is only ever called by add_room_key via
957        // handle_decrypted_to_device_event, so sender, sender_key, and algorithm are
958        // already recorded.
959        fields(room_id = ? content.room_id, session_id, message_index, shared_history = content.shared_history)
960    )]
961    async fn handle_key(
962        &self,
963        sender_key: Curve25519PublicKey,
964        event: &DecryptedRoomKeyEvent,
965        content: &MegolmV1AesSha2Content,
966    ) -> OlmResult<Option<InboundGroupSession>> {
967        let session =
968            InboundGroupSession::from_room_key_content(sender_key, event.keys.ed25519, content);
969
970        match session {
971            Ok(mut session) => {
972                Span::current().record("session_id", session.session_id());
973                Span::current().record("message_index", session.first_known_index());
974
975                let sender_data =
976                    SenderDataFinder::find_using_event(self.store(), sender_key, event, &session)
977                        .await?;
978                session.sender_data = sender_data;
979
980                Ok(self.store().merge_received_group_session(session).await?)
981            }
982            Err(e) => {
983                Span::current().record("session_id", &content.session_id);
984                warn!("Received a room key event which contained an invalid session key: {e}");
985
986                Ok(None)
987            }
988        }
989    }
990
991    /// Create a group session from a room key and add it to our crypto store.
992    #[instrument(skip_all, fields(algorithm = ?event.content.algorithm()))]
993    async fn add_room_key(
994        &self,
995        sender_key: Curve25519PublicKey,
996        event: &DecryptedRoomKeyEvent,
997    ) -> OlmResult<Option<InboundGroupSession>> {
998        match &event.content {
999            RoomKeyContent::MegolmV1AesSha2(content) => {
1000                self.handle_key(sender_key, event, content).await
1001            }
1002            #[cfg(feature = "experimental-algorithms")]
1003            RoomKeyContent::MegolmV2AesSha2(content) => {
1004                self.handle_key(sender_key, event, content).await
1005            }
1006            RoomKeyContent::Unknown(_) => {
1007                warn!("Received a room key with an unsupported algorithm");
1008                Ok(None)
1009            }
1010        }
1011    }
1012
1013    /// Handle a received, decrypted, `m.room_key_bundle` to-device event.
1014    #[instrument()]
1015    async fn receive_room_key_bundle_data(
1016        &self,
1017        sender_key: Curve25519PublicKey,
1018        event: &DecryptedRoomKeyBundleEvent,
1019        changes: &mut Changes,
1020    ) -> OlmResult<()> {
1021        let Some(sender_device_keys) = &event.sender_device_keys else {
1022            warn!("Received a room key bundle with no sender device keys: ignoring");
1023            return Ok(());
1024        };
1025
1026        // NOTE: We already checked that `sender_device_keys` matches the actual sender
1027        // of the message when we decrypted the message, which included doing
1028        // `DeviceData::try_from` on it, so it can't fail.
1029
1030        let sender_device_data =
1031            DeviceData::try_from(sender_device_keys).expect("failed to verify sender device keys");
1032        let sender_device = self.store().wrap_device_data(sender_device_data).await?;
1033
1034        changes.received_room_key_bundles.push(StoredRoomKeyBundleData {
1035            sender_user: event.sender.clone(),
1036            sender_data: SenderData::from_device(&sender_device),
1037            sender_key,
1038            bundle_data: event.content.clone(),
1039        });
1040        Ok(())
1041    }
1042
1043    fn add_withheld_info(&self, changes: &mut Changes, event: &RoomKeyWithheldEvent) {
1044        debug!(?event.content, "Processing `m.room_key.withheld` event");
1045
1046        if let RoomKeyWithheldContent::MegolmV1AesSha2(
1047            MegolmV1AesSha2WithheldContent::BlackListed(c)
1048            | MegolmV1AesSha2WithheldContent::Unverified(c)
1049            | MegolmV1AesSha2WithheldContent::Unauthorised(c)
1050            | MegolmV1AesSha2WithheldContent::Unavailable(c),
1051        ) = &event.content
1052        {
1053            changes
1054                .withheld_session_info
1055                .entry(c.room_id.to_owned())
1056                .or_default()
1057                .insert(c.session_id.to_owned(), event.to_owned().into());
1058        }
1059    }
1060
1061    #[cfg(test)]
1062    pub(crate) async fn create_outbound_group_session_with_defaults_test_helper(
1063        &self,
1064        room_id: &RoomId,
1065    ) -> OlmResult<()> {
1066        let (_, session) = self
1067            .inner
1068            .group_session_manager
1069            .create_outbound_group_session(
1070                room_id,
1071                EncryptionSettings::default(),
1072                SenderData::unknown(),
1073            )
1074            .await?;
1075
1076        self.store().save_inbound_group_sessions(&[session]).await?;
1077
1078        Ok(())
1079    }
1080
1081    #[cfg(test)]
1082    #[allow(dead_code)]
1083    pub(crate) async fn create_inbound_session_test_helper(
1084        &self,
1085        room_id: &RoomId,
1086    ) -> OlmResult<InboundGroupSession> {
1087        let (_, session) = self
1088            .inner
1089            .group_session_manager
1090            .create_outbound_group_session(
1091                room_id,
1092                EncryptionSettings::default(),
1093                SenderData::unknown(),
1094            )
1095            .await?;
1096
1097        Ok(session)
1098    }
1099
1100    /// Encrypt a room message for the given room.
1101    ///
1102    /// Beware that a room key needs to be shared before this method
1103    /// can be called using the [`OlmMachine::share_room_key`] method.
1104    ///
1105    /// # Arguments
1106    ///
1107    /// * `room_id` - The id of the room for which the message should be
1108    ///   encrypted.
1109    ///
1110    /// * `content` - The plaintext content of the message that should be
1111    ///   encrypted.
1112    ///
1113    /// # Panics
1114    ///
1115    /// Panics if a room key for the given room wasn't shared beforehand.
1116    pub async fn encrypt_room_event(
1117        &self,
1118        room_id: &RoomId,
1119        content: impl MessageLikeEventContent,
1120    ) -> MegolmResult<RawEncryptionResult> {
1121        let event_type = content.event_type().to_string();
1122        let content = Raw::new(&content)?.cast_unchecked();
1123        self.encrypt_room_event_raw(room_id, &event_type, &content).await
1124    }
1125
1126    /// Encrypt a raw JSON content for the given room.
1127    ///
1128    /// This method is equivalent to the [`OlmMachine::encrypt_room_event()`]
1129    /// method but operates on an arbitrary JSON value instead of strongly-typed
1130    /// event content struct.
1131    ///
1132    /// # Arguments
1133    ///
1134    /// * `room_id` - The id of the room for which the message should be
1135    ///   encrypted.
1136    ///
1137    /// * `content` - The plaintext content of the message that should be
1138    ///   encrypted as a raw JSON value.
1139    ///
1140    /// * `event_type` - The plaintext type of the event.
1141    ///
1142    /// # Panics
1143    ///
1144    /// Panics if a group session for the given room wasn't shared beforehand.
1145    pub async fn encrypt_room_event_raw(
1146        &self,
1147        room_id: &RoomId,
1148        event_type: &str,
1149        content: &Raw<AnyMessageLikeEventContent>,
1150    ) -> MegolmResult<RawEncryptionResult> {
1151        self.inner.group_session_manager.encrypt(room_id, event_type, content).await.map(|result| {
1152            RawEncryptionResult {
1153                content: result.content,
1154                encryption_info: self
1155                    .own_encryption_info(result.algorithm, result.session_id.to_string()),
1156            }
1157        })
1158    }
1159
1160    fn own_encryption_info(
1161        &self,
1162        algorithm: EventEncryptionAlgorithm,
1163        session_id: String,
1164    ) -> EncryptionInfo {
1165        let identity_keys = self.identity_keys();
1166
1167        let algorithm_info = match algorithm {
1168            EventEncryptionAlgorithm::MegolmV1AesSha2 => AlgorithmInfo::MegolmV1AesSha2 {
1169                curve25519_key: identity_keys.curve25519.to_base64(),
1170                sender_claimed_keys: BTreeMap::from([(
1171                    DeviceKeyAlgorithm::Ed25519,
1172                    identity_keys.ed25519.to_base64(),
1173                )]),
1174                session_id: Some(session_id),
1175            },
1176            EventEncryptionAlgorithm::OlmV1Curve25519AesSha2 => {
1177                AlgorithmInfo::OlmV1Curve25519AesSha2 {
1178                    curve25519_public_key_base64: identity_keys.curve25519.to_base64(),
1179                }
1180            }
1181            _ => unreachable!(
1182                "Only MegolmV1AesSha2 and OlmV1Curve25519AesSha2 are supported on this level"
1183            ),
1184        };
1185
1186        EncryptionInfo {
1187            sender: self.inner.user_id.clone(),
1188            sender_device: Some(self.inner.device_id.clone()),
1189            forwarder: None,
1190            algorithm_info,
1191            verification_state: VerificationState::Verified,
1192        }
1193    }
1194
1195    /// Encrypt a state event for the given room.
1196    ///
1197    /// # Arguments
1198    ///
1199    /// * `room_id` - The id of the room for which the event should be
1200    ///   encrypted.
1201    ///
1202    /// * `content` - The plaintext content of the event that should be
1203    ///   encrypted.
1204    ///
1205    /// * `state_key` - The associated state key of the event.
1206    #[cfg(feature = "experimental-encrypted-state-events")]
1207    pub async fn encrypt_state_event<C, K>(
1208        &self,
1209        room_id: &RoomId,
1210        content: C,
1211        state_key: K,
1212    ) -> MegolmResult<Raw<RoomEncryptedEventContent>>
1213    where
1214        C: StateEventContent,
1215        C::StateKey: Borrow<K>,
1216        K: AsRef<str>,
1217    {
1218        let event_type = content.event_type().to_string();
1219        let content = Raw::new(&content)?.cast_unchecked();
1220        self.encrypt_state_event_raw(room_id, &event_type, state_key.as_ref(), &content).await
1221    }
1222
1223    /// Encrypt a state event for the given state event using its raw JSON
1224    /// content and state key.
1225    ///
1226    /// This method is equivalent to [`OlmMachine::encrypt_state_event`]
1227    /// method but operates on an arbitrary JSON value instead of strongly-typed
1228    /// event content struct.
1229    ///
1230    /// # Arguments
1231    ///
1232    /// * `room_id` - The id of the room for which the message should be
1233    ///   encrypted.
1234    ///
1235    /// * `event_type` - The type of the event.
1236    ///
1237    /// * `state_key` - The associated state key of the event.
1238    ///
1239    /// * `content` - The plaintext content of the event that should be
1240    ///   encrypted as a raw JSON value.
1241    #[cfg(feature = "experimental-encrypted-state-events")]
1242    pub async fn encrypt_state_event_raw(
1243        &self,
1244        room_id: &RoomId,
1245        event_type: &str,
1246        state_key: &str,
1247        content: &Raw<AnyStateEventContent>,
1248    ) -> MegolmResult<Raw<RoomEncryptedEventContent>> {
1249        self.inner
1250            .group_session_manager
1251            .encrypt_state(room_id, event_type, state_key, content)
1252            .await
1253    }
1254
1255    /// Forces the currently active room key, which is used to encrypt messages,
1256    /// to be rotated.
1257    ///
1258    /// A new room key will be crated and shared with all the room members the
1259    /// next time a message will be sent. You don't have to call this method,
1260    /// room keys will be rotated automatically when necessary. This method is
1261    /// still useful for debugging purposes.
1262    ///
1263    /// Returns true if a session was invalidated, false if there was no session
1264    /// to invalidate.
1265    pub async fn discard_room_key(&self, room_id: &RoomId) -> StoreResult<bool> {
1266        self.inner.group_session_manager.invalidate_group_session(room_id).await
1267    }
1268
1269    /// Get to-device requests to share a room key with users in a room.
1270    ///
1271    /// # Arguments
1272    ///
1273    /// `room_id` - The room id of the room where the room key will be
1274    /// used.
1275    ///
1276    /// `users` - The list of users that should receive the room key.
1277    ///
1278    /// `settings` - Encryption settings that affect when are room keys rotated
1279    /// and who are they shared with.
1280    ///
1281    /// # Returns
1282    ///
1283    /// List of the to-device requests that need to be sent out to the server
1284    /// and the responses need to be passed back to the state machine with
1285    /// [`mark_request_as_sent`], using the to-device `txn_id` as `request_id`.
1286    ///
1287    /// [`mark_request_as_sent`]: #method.mark_request_as_sent
1288    pub async fn share_room_key(
1289        &self,
1290        room_id: &RoomId,
1291        users: impl Iterator<Item = &UserId>,
1292        encryption_settings: impl Into<EncryptionSettings>,
1293    ) -> OlmResult<Vec<Arc<ToDeviceRequest>>> {
1294        self.inner.group_session_manager.share_room_key(room_id, users, encryption_settings).await
1295    }
1296
1297    /// Encrypts the given content using Olm for each of the given devices.
1298    ///
1299    /// The 1-to-1 session must be established prior to this
1300    /// call by using the [`OlmMachine::get_missing_sessions`] method or the
1301    /// encryption will fail.
1302    ///
1303    /// The caller is responsible for sending the encrypted
1304    /// event to the target device, and should do it ASAP to avoid out-of-order
1305    /// messages.
1306    ///
1307    /// # Returns
1308    /// A list of `ToDeviceRequest` to send out the event, and the list of
1309    /// devices where encryption did not succeed (device excluded or no olm)
1310    #[cfg(feature = "experimental-send-custom-to-device")]
1311    pub async fn encrypt_content_for_devices(
1312        &self,
1313        devices: Vec<DeviceData>,
1314        event_type: &str,
1315        content: &Value,
1316        share_strategy: CollectStrategy,
1317    ) -> OlmResult<(Vec<ToDeviceRequest>, Vec<(DeviceData, WithheldCode)>)> {
1318        let mut changes = Changes::default();
1319
1320        let (allowed_devices, mut blocked_devices) =
1321            split_devices_for_share_strategy(&self.inner.store, devices, share_strategy).await?;
1322
1323        let result = self
1324            .inner
1325            .group_session_manager
1326            .encrypt_content_for_devices(allowed_devices, event_type, content.clone(), &mut changes)
1327            .await;
1328
1329        // Persist any changes we might have collected.
1330        if !changes.is_empty() {
1331            let session_count = changes.sessions.len();
1332
1333            self.inner.store.save_changes(changes).await?;
1334
1335            trace!(
1336                session_count = session_count,
1337                "Stored the changed sessions after encrypting a custom to-device event"
1338            );
1339        }
1340
1341        result.map(|(to_device_requests, mut withheld)| {
1342            withheld.append(&mut blocked_devices);
1343            (to_device_requests, withheld)
1344        })
1345    }
1346    /// Collect the devices belonging to the given user, and send the details of
1347    /// a room key bundle to those devices.
1348    ///
1349    /// Returns a list of to-device requests which must be sent.
1350    pub async fn share_room_key_bundle_data(
1351        &self,
1352        user_id: &UserId,
1353        collect_strategy: &CollectStrategy,
1354        bundle_data: RoomKeyBundleContent,
1355    ) -> OlmResult<Vec<ToDeviceRequest>> {
1356        self.inner
1357            .group_session_manager
1358            .share_room_key_bundle_data(user_id, collect_strategy, bundle_data)
1359            .await
1360    }
1361
1362    /// Receive an unencrypted verification event.
1363    ///
1364    /// This method can be used to pass verification events that are happening
1365    /// in unencrypted rooms to the `OlmMachine`.
1366    ///
1367    /// **Note**: This does not need to be called for encrypted events since
1368    /// those will get passed to the `OlmMachine` during decryption.
1369    #[deprecated(note = "Use OlmMachine::receive_verification_event instead", since = "0.7.0")]
1370    pub async fn receive_unencrypted_verification_event(
1371        &self,
1372        event: &AnyMessageLikeEvent,
1373    ) -> StoreResult<()> {
1374        self.inner.verification_machine.receive_any_event(event).await
1375    }
1376
1377    /// Receive a verification event.
1378    ///
1379    /// The event should be in the decrypted form.
1380    ///
1381    /// **Note**: If the supplied event is an `m.room.message` event with
1382    /// `msgtype: m.key.verification.request`, then the device information for
1383    /// the sending user must be up-to-date before calling this method
1384    /// (otherwise, the request will be ignored). It is hard to guarantee this
1385    /// is the case, but you can maximize your chances by explicitly making a
1386    /// request for this user's device info by calling
1387    /// [`OlmMachine::query_keys_for_users`], sending the request, and
1388    /// processing the response with [`OlmMachine::mark_request_as_sent`].
1389    pub async fn receive_verification_event(&self, event: &AnyMessageLikeEvent) -> StoreResult<()> {
1390        self.inner.verification_machine.receive_any_event(event).await
1391    }
1392
1393    /// Receive and properly handle a decrypted to-device event.
1394    ///
1395    /// # Arguments
1396    ///
1397    /// * `decrypted` - The decrypted event and some associated metadata.
1398    #[instrument(
1399        skip_all,
1400        fields(
1401            sender_key = ?decrypted.result.sender_key,
1402            event_type = decrypted.result.event.event_type(),
1403        ),
1404    )]
1405    async fn handle_decrypted_to_device_event(
1406        &self,
1407        cache: &StoreCache,
1408        decrypted: &mut OlmDecryptionInfo,
1409        changes: &mut Changes,
1410    ) -> OlmResult<()> {
1411        debug!(
1412            sender_device_keys =
1413                ?decrypted.result.event.sender_device_keys().map(|k| (k.curve25519_key(), k.ed25519_key())).unwrap_or((None, None)),
1414            "Received a decrypted to-device event",
1415        );
1416
1417        match &*decrypted.result.event {
1418            AnyDecryptedOlmEvent::RoomKey(e) => {
1419                let session = self.add_room_key(decrypted.result.sender_key, e).await?;
1420                decrypted.inbound_group_session = session;
1421            }
1422            AnyDecryptedOlmEvent::ForwardedRoomKey(e) => {
1423                let session = self
1424                    .inner
1425                    .key_request_machine
1426                    .receive_forwarded_room_key(decrypted.result.sender_key, e)
1427                    .await?;
1428                decrypted.inbound_group_session = session;
1429            }
1430            AnyDecryptedOlmEvent::SecretSend(e) => {
1431                let name = self
1432                    .inner
1433                    .key_request_machine
1434                    .receive_secret_event(cache, decrypted.result.sender_key, e, changes)
1435                    .await?;
1436
1437                // Set the secret name so other consumers of the event know
1438                // what this event is about.
1439                if let Ok(ToDeviceEvents::SecretSend(mut e)) =
1440                    decrypted.result.raw_event.deserialize_as()
1441                {
1442                    e.content.secret_name = name;
1443                    decrypted.result.raw_event = Raw::from_json(to_raw_value(&e)?);
1444                }
1445
1446                if enabled!(tracing::Level::DEBUG) {
1447                    let cross_signing_status = self.cross_signing_status().await;
1448                    let backup_enabled = self.backup_machine().enabled().await;
1449                    debug!(
1450                        ?cross_signing_status,
1451                        backup_enabled, "Status after receiving secret event"
1452                    );
1453                }
1454            }
1455            AnyDecryptedOlmEvent::Dummy(_) => {
1456                debug!("Received an `m.dummy` event");
1457            }
1458            AnyDecryptedOlmEvent::RoomKeyBundle(e) => {
1459                debug!("Received a room key bundle event {:?}", e);
1460                self.receive_room_key_bundle_data(decrypted.result.sender_key, e, changes).await?;
1461            }
1462            #[cfg(feature = "experimental-push-secrets")]
1463            AnyDecryptedOlmEvent::SecretPush(e) => {
1464                self.inner
1465                    .key_request_machine
1466                    .receive_secret_push_event(&decrypted.result.sender_key, e, changes)
1467                    .await?;
1468            }
1469            AnyDecryptedOlmEvent::Custom(_) => {
1470                warn!("Received an unexpected encrypted to-device event");
1471            }
1472        }
1473
1474        Ok(())
1475    }
1476
1477    async fn handle_verification_event(&self, event: &ToDeviceEvents) {
1478        if let Err(e) = self.inner.verification_machine.receive_any_event(event).await {
1479            error!("Error handling a verification event: {e:?}");
1480        }
1481    }
1482
1483    /// Mark an outgoing to-device requests as sent.
1484    async fn mark_to_device_request_as_sent(&self, request_id: &TransactionId) -> StoreResult<()> {
1485        self.inner.verification_machine.mark_request_as_sent(request_id);
1486        self.inner.key_request_machine.mark_outgoing_request_as_sent(request_id).await?;
1487        self.inner.group_session_manager.mark_request_as_sent(request_id).await?;
1488        self.inner.session_manager.mark_outgoing_request_as_sent(request_id);
1489        Ok(())
1490    }
1491
1492    /// Get a verification object for the given user id with the given flow id.
1493    pub fn get_verification(&self, user_id: &UserId, flow_id: &str) -> Option<Verification> {
1494        self.inner.verification_machine.get_verification(user_id, flow_id)
1495    }
1496
1497    /// Get a verification request object with the given flow id.
1498    pub fn get_verification_request(
1499        &self,
1500        user_id: &UserId,
1501        flow_id: impl AsRef<str>,
1502    ) -> Option<VerificationRequest> {
1503        self.inner.verification_machine.get_request(user_id, flow_id)
1504    }
1505
1506    /// Get all the verification requests of a given user.
1507    pub fn get_verification_requests(&self, user_id: &UserId) -> Vec<VerificationRequest> {
1508        self.inner.verification_machine.get_requests(user_id)
1509    }
1510
1511    /// Given a to-device event that has either been decrypted or arrived in
1512    /// plaintext, handle it.
1513    ///
1514    /// Here, we only process events that are allowed to arrive in plaintext.
1515    async fn handle_to_device_event(&self, changes: &mut Changes, event: &ToDeviceEvents) {
1516        use crate::types::events::ToDeviceEvents::*;
1517
1518        match event {
1519            // These are handled here because we accept them either plaintext or
1520            // encrypted.
1521            //
1522            // Note: this list should match the allowed types in
1523            // check_to_device_is_from_verified_device_or_allowed_type
1524            RoomKeyRequest(e) => self.inner.key_request_machine.receive_incoming_key_request(e),
1525            SecretRequest(e) => self.inner.key_request_machine.receive_incoming_secret_request(e),
1526            RoomKeyWithheld(e) => self.add_withheld_info(changes, e),
1527            KeyVerificationAccept(..)
1528            | KeyVerificationCancel(..)
1529            | KeyVerificationKey(..)
1530            | KeyVerificationMac(..)
1531            | KeyVerificationRequest(..)
1532            | KeyVerificationReady(..)
1533            | KeyVerificationDone(..)
1534            | KeyVerificationStart(..) => {
1535                self.handle_verification_event(event).await;
1536            }
1537
1538            // We don't process custom or dummy events at all
1539            Custom(_) | Dummy(_) => {}
1540
1541            // Encrypted events are handled elsewhere
1542            RoomEncrypted(_) => {}
1543
1544            // These are handled in `handle_decrypted_to_device_event` because we
1545            // only accept them if they arrive encrypted.
1546            SecretSend(_) | RoomKey(_) | ForwardedRoomKey(_) => {}
1547        }
1548    }
1549
1550    fn record_message_id(event: &Raw<AnyToDeviceEvent>) {
1551        use serde::Deserialize;
1552
1553        #[derive(Deserialize)]
1554        struct ContentStub<'a> {
1555            #[serde(borrow, rename = "org.matrix.msgid")]
1556            message_id: Option<&'a str>,
1557        }
1558        #[derive(Deserialize)]
1559        struct ToDeviceStub<'a> {
1560            sender: &'a str,
1561            #[serde(rename = "type")]
1562            event_type: &'a str,
1563            #[serde(borrow)]
1564            content: ContentStub<'a>,
1565        }
1566
1567        if let Ok(event) = event.deserialize_as_unchecked::<ToDeviceStub<'_>>() {
1568            Span::current().record("sender", event.sender);
1569            Span::current().record("event_type", event.event_type);
1570            Span::current().record("message_id", event.content.message_id);
1571        }
1572    }
1573
1574    /// Decrypt the supplied to-device event (if needed, and if we can) and
1575    /// handle it.
1576    ///
1577    /// Return the same event, decrypted if possible and needed.
1578    ///
1579    /// If we can identify that this to-device event came from a dehydrated
1580    /// device, this method does not process it, and returns `None`.
1581    #[instrument(skip_all, fields(sender, event_type, message_id))]
1582    async fn receive_to_device_event(
1583        &self,
1584        transaction: &mut StoreTransaction,
1585        changes: &mut Changes,
1586        raw_event: Raw<AnyToDeviceEvent>,
1587        decryption_settings: &DecryptionSettings,
1588    ) -> Option<ProcessedToDeviceEvent> {
1589        Self::record_message_id(&raw_event);
1590
1591        let event: ToDeviceEvents = match raw_event.deserialize_as() {
1592            Ok(e) => e,
1593            Err(e) => {
1594                // Skip invalid events.
1595                warn!("Received an invalid to-device event: {e}");
1596                return Some(ProcessedToDeviceEvent::Invalid(raw_event));
1597            }
1598        };
1599
1600        debug!("Received a to-device event");
1601
1602        match event {
1603            ToDeviceEvents::RoomEncrypted(e) => {
1604                self.receive_encrypted_to_device_event(
1605                    transaction,
1606                    changes,
1607                    raw_event,
1608                    e,
1609                    decryption_settings,
1610                )
1611                .await
1612            }
1613            e => {
1614                self.handle_to_device_event(changes, &e).await;
1615                Some(ProcessedToDeviceEvent::PlainText(raw_event))
1616            }
1617        }
1618    }
1619
1620    /// Decrypt the supplied encrypted to-device event (if we can) and handle
1621    /// it.
1622    ///
1623    /// Return the same event, decrypted if possible.
1624    ///
1625    /// If we are in strict "exclude insecure devices" mode and the sender
1626    /// device is not verified, and the decrypted event type is not on the
1627    /// allow list, or if this event comes from a dehydrated device, this method
1628    /// does not process it, and returns `None`.
1629    ///
1630    /// (The allow list of types that are processed even if the sender is
1631    /// unverified is: `m.room_key`, `m.room_key.withheld`,
1632    /// `m.room_key_request`, `m.secret.request` and `m.key.verification.*`.)
1633    async fn receive_encrypted_to_device_event(
1634        &self,
1635        transaction: &mut StoreTransaction,
1636        changes: &mut Changes,
1637        mut raw_event: Raw<AnyToDeviceEvent>,
1638        e: ToDeviceEvent<ToDeviceEncryptedEventContent>,
1639        decryption_settings: &DecryptionSettings,
1640    ) -> Option<ProcessedToDeviceEvent> {
1641        let decrypted = match self
1642            .decrypt_to_device_event(transaction, &e, changes, decryption_settings)
1643            .await
1644        {
1645            Ok(decrypted) => decrypted,
1646            Err(DecryptToDeviceError::OlmError(err)) => {
1647                let reason = if let OlmError::UnverifiedSenderDevice = &err {
1648                    ToDeviceUnableToDecryptReason::UnverifiedSenderDevice
1649                } else {
1650                    ToDeviceUnableToDecryptReason::DecryptionFailure
1651                };
1652
1653                if let OlmError::SessionWedged(sender, curve_key) = err
1654                    && let Err(e) =
1655                        self.inner.session_manager.mark_device_as_wedged(&sender, curve_key).await
1656                {
1657                    error!(
1658                        error = ?e,
1659                        "Couldn't mark device to be unwedged",
1660                    );
1661                }
1662
1663                return Some(ProcessedToDeviceEvent::UnableToDecrypt {
1664                    encrypted_event: raw_event,
1665                    utd_info: ToDeviceUnableToDecryptInfo { reason },
1666                });
1667            }
1668            Err(DecryptToDeviceError::FromDehydratedDevice) => return None,
1669        };
1670
1671        // New sessions modify the account so we need to save that
1672        // one as well.
1673        match decrypted.session {
1674            SessionType::New(s) | SessionType::Existing(s) => {
1675                changes.sessions.push(s);
1676            }
1677        }
1678
1679        changes.message_hashes.push(decrypted.message_hash);
1680
1681        if let Some(group_session) = decrypted.inbound_group_session {
1682            changes.inbound_group_sessions.push(group_session);
1683        }
1684
1685        match decrypted.result.raw_event.deserialize_as() {
1686            Ok(event) => {
1687                self.handle_to_device_event(changes, &event).await;
1688
1689                raw_event = event
1690                    .serialize_zeroized()
1691                    .expect("Zeroizing and reserializing our events should always work")
1692                    .cast();
1693            }
1694            Err(e) => {
1695                warn!("Received an invalid encrypted to-device event: {e}");
1696                raw_event = decrypted.result.raw_event;
1697            }
1698        }
1699
1700        Some(ProcessedToDeviceEvent::Decrypted {
1701            raw: raw_event,
1702            encryption_info: decrypted.result.encryption_info,
1703        })
1704    }
1705
1706    /// Return an error if the supplied to-device event was sent from a
1707    /// dehydrated device.
1708    async fn check_to_device_event_is_not_from_dehydrated_device(
1709        &self,
1710        decrypted: &OlmDecryptionInfo,
1711        sender_user_id: &UserId,
1712    ) -> Result<(), DecryptToDeviceError> {
1713        if self.to_device_event_is_from_dehydrated_device(decrypted, sender_user_id).await? {
1714            warn!(
1715                sender = ?sender_user_id,
1716                session = ?decrypted.session,
1717                "Received a to-device event from a dehydrated device. This is unexpected: ignoring event"
1718            );
1719            Err(DecryptToDeviceError::FromDehydratedDevice)
1720        } else {
1721            Ok(())
1722        }
1723    }
1724
1725    /// Decide whether a decrypted to-device event was sent from a dehydrated
1726    /// device.
1727    ///
1728    /// This accepts an [`OlmDecryptionInfo`] because it deals with a decrypted
1729    /// event.
1730    async fn to_device_event_is_from_dehydrated_device(
1731        &self,
1732        decrypted: &OlmDecryptionInfo,
1733        sender_user_id: &UserId,
1734    ) -> OlmResult<bool> {
1735        // Does the to-device message include device info?
1736        if let Some(device_keys) = decrypted.result.event.sender_device_keys() {
1737            // There is no need to check whether the device keys are signed correctly - any
1738            // to-device message that claims to be from a dehydrated device is weird, so we
1739            // will drop it.
1740
1741            // Does the included device info say the device is dehydrated?
1742            if device_keys.dehydrated.unwrap_or(false) {
1743                return Ok(true);
1744            }
1745            // If not, fall through and check our existing list of devices
1746            // below, just in case the sender is sending us incorrect
1747            // information embedded in the to-device message, but we know
1748            // better.
1749        }
1750
1751        // Do we already know about this device?
1752        Ok(self
1753            .store()
1754            .get_device_from_curve_key(sender_user_id, decrypted.result.sender_key)
1755            .await?
1756            .is_some_and(|d| d.is_dehydrated()))
1757    }
1758
1759    /// Handle a to-device and one-time key counts from a sync response.
1760    ///
1761    /// This will decrypt and handle to-device events returning the decrypted
1762    /// versions of them.
1763    ///
1764    /// To decrypt an event from the room timeline, call [`decrypt_room_event`].
1765    ///
1766    /// # Arguments
1767    ///
1768    /// * `sync_changes` - an [`EncryptionSyncChanges`] value, constructed from
1769    ///   a sync response.
1770    ///
1771    /// [`decrypt_room_event`]: #method.decrypt_room_event
1772    ///
1773    /// # Returns
1774    ///
1775    /// A tuple of (decrypted to-device events, updated room keys).
1776    #[instrument(skip_all)]
1777    pub async fn receive_sync_changes(
1778        &self,
1779        sync_changes: EncryptionSyncChanges<'_>,
1780        decryption_settings: &DecryptionSettings,
1781    ) -> OlmResult<(Vec<ProcessedToDeviceEvent>, Vec<RoomKeyInfo>)> {
1782        let mut store_transaction = self.inner.store.transaction().await;
1783
1784        let (events, changes) = self
1785            .preprocess_sync_changes(&mut store_transaction, sync_changes, decryption_settings)
1786            .await?;
1787
1788        // Technically save_changes also does the same work, so if it's slow we could
1789        // refactor this to do it only once.
1790        let room_key_updates: Vec<_> =
1791            changes.inbound_group_sessions.iter().map(RoomKeyInfo::from).collect();
1792
1793        self.store().save_changes(changes).await?;
1794        store_transaction.commit().await?;
1795
1796        Ok((events, room_key_updates))
1797    }
1798
1799    /// Initial processing of the changes specified within a sync response.
1800    ///
1801    /// Returns the to-device events (decrypted where needed and where possible)
1802    /// and the processed set of changes.
1803    ///
1804    /// If any of the to-device events in the supplied changes were sent from
1805    /// dehydrated devices, these are not processed, and are omitted from
1806    /// the returned list, as per MSC3814.
1807    ///
1808    /// If we are in strict "exclude insecure devices" mode and the sender
1809    /// device of any event is not verified, and the decrypted event type is not
1810    /// on the allow list, these events are not processed and are omitted from
1811    /// the returned list.
1812    ///
1813    /// (The allow list of types that are processed even if the sender is
1814    /// unverified is: `m.room_key`, `m.room_key.withheld`,
1815    /// `m.room_key_request`, `m.secret.request` and `m.key.verification.*`.)
1816    pub(crate) async fn preprocess_sync_changes(
1817        &self,
1818        transaction: &mut StoreTransaction,
1819        sync_changes: EncryptionSyncChanges<'_>,
1820        decryption_settings: &DecryptionSettings,
1821    ) -> OlmResult<(Vec<ProcessedToDeviceEvent>, Changes)> {
1822        // Remove verification objects that have expired or are done.
1823        let mut events: Vec<ProcessedToDeviceEvent> = self
1824            .inner
1825            .verification_machine
1826            .garbage_collect()
1827            .iter()
1828            // These are `fake` to device events just serving as local echo
1829            // in order that our own client can react quickly to cancelled transaction.
1830            // Just use PlainText for that.
1831            .map(|e| ProcessedToDeviceEvent::PlainText(e.clone()))
1832            .collect();
1833        // The account is automatically saved by the store transaction created by the
1834        // caller.
1835        let mut changes = Default::default();
1836
1837        {
1838            let account = transaction.account().await?;
1839            account.update_key_counts(
1840                sync_changes.one_time_keys_counts,
1841                sync_changes.unused_fallback_keys,
1842            )
1843        }
1844
1845        if let Err(e) = self
1846            .inner
1847            .identity_manager
1848            .receive_device_changes(
1849                transaction.cache(),
1850                sync_changes.changed_devices.changed.iter().map(|u| u.as_ref()),
1851            )
1852            .await
1853        {
1854            error!(error = ?e, "Error marking a tracked user as changed");
1855        }
1856
1857        for raw_event in sync_changes.to_device_events {
1858            let processed_event = Box::pin(self.receive_to_device_event(
1859                transaction,
1860                &mut changes,
1861                raw_event,
1862                decryption_settings,
1863            ))
1864            .await;
1865
1866            if let Some(processed_event) = processed_event {
1867                events.push(processed_event);
1868            }
1869        }
1870
1871        let changed_sessions = self
1872            .inner
1873            .key_request_machine
1874            .collect_incoming_key_requests(transaction.cache())
1875            .await?;
1876
1877        changes.sessions.extend(changed_sessions);
1878        changes.next_batch_token = sync_changes.next_batch_token;
1879
1880        Ok((events, changes))
1881    }
1882
1883    /// Request a room key from our devices.
1884    ///
1885    /// This method will return a request cancellation and a new key request if
1886    /// the key was already requested, otherwise it will return just the key
1887    /// request.
1888    ///
1889    /// The request cancellation *must* be sent out before the request is sent
1890    /// out, otherwise devices will ignore the key request.
1891    ///
1892    /// # Arguments
1893    ///
1894    /// * `room_id` - The id of the room where the key is used in.
1895    ///
1896    /// * `sender_key` - The curve25519 key of the sender that owns the key.
1897    ///
1898    /// * `session_id` - The id that uniquely identifies the session.
1899    pub async fn request_room_key(
1900        &self,
1901        event: &Raw<EncryptedEvent>,
1902        room_id: &RoomId,
1903    ) -> MegolmResult<(Option<OutgoingRequest>, OutgoingRequest)> {
1904        let event = event.deserialize()?;
1905        self.inner.key_request_machine.request_key(room_id, &event).await
1906    }
1907
1908    /// Find whether an event decrypted via the supplied session is verified,
1909    /// and provide explanation of what is missing/wrong if not.
1910    ///
1911    /// Stores the updated [`SenderData`] for the session in the store
1912    /// if we find an updated value for it.
1913    ///
1914    /// # Arguments
1915    ///
1916    /// * `session` - The inbound Megolm session that was used to decrypt the
1917    ///   event.
1918    /// * `sender` - The `sender` of that event (as claimed by the envelope of
1919    ///   the event).
1920    async fn get_room_event_verification_state(
1921        &self,
1922        session: &InboundGroupSession,
1923        sender: &UserId,
1924    ) -> MegolmResult<(VerificationState, Option<OwnedDeviceId>)> {
1925        let sender_data = self.get_or_update_sender_data(session, sender).await?;
1926
1927        // If the user ID in the sender data doesn't match that in the event envelope,
1928        // this event is not from who it appears to be from.
1929        //
1930        // If `sender_data.user_id()` returns `None`, that means we don't have any
1931        // information about the owner of the session (i.e. we have
1932        // `SenderData::UnknownDevice`); in that case we fall through to the
1933        // logic in `sender_data_to_verification_state` which will pick an appropriate
1934        // `DeviceLinkProblem` for `VerificationLevel::None`.
1935        let (verification_state, device_id) = match sender_data.user_id() {
1936            Some(i) if i != sender => {
1937                (VerificationState::Unverified(VerificationLevel::MismatchedSender), None)
1938            }
1939
1940            Some(_) | None => {
1941                sender_data_to_verification_state(sender_data, session.has_been_imported())
1942            }
1943        };
1944
1945        Ok((verification_state, device_id))
1946    }
1947
1948    /// Get an up-to-date [`SenderData`] for the given session, suitable for
1949    /// determining if messages decrypted using that session are verified.
1950    ///
1951    /// Checks both the stored verification state of the session and a
1952    /// recalculated verification state based on our current knowledge, and
1953    /// returns the more trusted of the two.
1954    ///
1955    /// Stores the updated [`SenderData`] for the session in the store
1956    /// if we find an updated value for it.
1957    ///
1958    /// # Arguments
1959    ///
1960    /// * `session` - The Megolm session that was used to decrypt the event.
1961    /// * `sender` - The claimed sender of that event.
1962    async fn get_or_update_sender_data(
1963        &self,
1964        session: &InboundGroupSession,
1965        sender: &UserId,
1966    ) -> MegolmResult<SenderData> {
1967        let sender_data = if session.sender_data.should_recalculate() {
1968            // The session is not sure of the sender yet. Try to find a matching device
1969            // belonging to the claimed sender of the recently-received event.
1970            //
1971            // It's worth noting that this could in theory result in unintuitive changes,
1972            // like a session which initially appears to belong to Alice turning into a
1973            // session which belongs to Bob [1]. This could mean that a session initially
1974            // successfully decrypts events from Alice, but then stops decrypting those same
1975            // events once we get an update.
1976            //
1977            // That's ok though: if we get good evidence that the session belongs to Bob,
1978            // it's correct to update the session even if we previously had weak
1979            // evidence it belonged to Alice.
1980            //
1981            // [1] For example: maybe Alice and Bob both publish devices with the *same*
1982            // keys (presumably because they are colluding). Initially we think
1983            // the session belongs to Alice, but then we do a device lookup for
1984            // Bob, we find a matching device with a cross-signature, so prefer
1985            // that.
1986            let calculated_sender_data = SenderDataFinder::find_using_curve_key(
1987                self.store(),
1988                session.sender_key(),
1989                sender,
1990                session,
1991            )
1992            .await?;
1993
1994            // Is the newly-calculated sender data more trusted?
1995            if calculated_sender_data.compare_trust_level(&session.sender_data).is_gt() {
1996                // Yes - save it to the store
1997                let mut new_session = session.clone();
1998                new_session.sender_data = calculated_sender_data.clone();
1999                self.store().save_inbound_group_sessions(&[new_session]).await?;
2000
2001                // and use it now.
2002                calculated_sender_data
2003            } else {
2004                // No - use the existing data.
2005                session.sender_data.clone()
2006            }
2007        } else {
2008            session.sender_data.clone()
2009        };
2010
2011        Ok(sender_data)
2012    }
2013
2014    /// Request missing local secrets from our devices (cross signing private
2015    /// keys, megolm backup). This will ask the sdk to create outgoing
2016    /// request to get the missing secrets.
2017    ///
2018    /// The requests will be processed as soon as `outgoing_requests()` is
2019    /// called to process them.
2020    ///
2021    /// # Returns
2022    ///
2023    /// A bool result saying if actual secrets were missing and have been
2024    /// requested
2025    ///
2026    /// # Examples
2027    //
2028    /// ```
2029    /// # async {
2030    /// # use matrix_sdk_crypto::OlmMachine;
2031    /// # let machine: OlmMachine = unimplemented!();
2032    /// if machine.query_missing_secrets_from_other_sessions().await.unwrap() {
2033    ///     let to_send = machine.outgoing_requests().await.unwrap();
2034    ///     // send the to device requests
2035    /// };
2036    /// # anyhow::Ok(()) };
2037    /// ```
2038    pub async fn query_missing_secrets_from_other_sessions(&self) -> StoreResult<bool> {
2039        let identity = self.inner.user_identity.lock().await;
2040        let mut secrets = identity.get_missing_secrets().await;
2041
2042        if self.store().load_backup_keys().await?.decryption_key.is_none() {
2043            secrets.push(SecretName::RecoveryKey);
2044        }
2045
2046        if secrets.is_empty() {
2047            debug!("No missing requests to query");
2048            return Ok(false);
2049        }
2050
2051        let secret_requests = GossipMachine::request_missing_secrets(self.user_id(), secrets);
2052
2053        // Check if there are already in-flight requests for these secrets?
2054        let unsent_request = self.store().get_unsent_secret_requests().await?;
2055        let not_yet_requested = secret_requests
2056            .into_iter()
2057            .filter(|request| !unsent_request.iter().any(|unsent| unsent.info == request.info))
2058            .collect_vec();
2059
2060        if not_yet_requested.is_empty() {
2061            debug!("The missing secrets have already been requested");
2062            Ok(false)
2063        } else {
2064            debug!("Requesting missing secrets");
2065
2066            let changes = Changes { key_requests: not_yet_requested, ..Default::default() };
2067
2068            self.store().save_changes(changes).await?;
2069            Ok(true)
2070        }
2071    }
2072
2073    /// Push a secret to all of our other verified devices.
2074    ///
2075    /// This function assumes that we already have Olm sessions with the other
2076    /// devices.  This can be done by calling
2077    /// [`OlmMachine::get_missing_sessions()`].
2078    ///
2079    /// * `secret_name` - The name of the secret to push
2080    #[cfg(feature = "experimental-push-secrets")]
2081    pub async fn push_secret_to_verified_devices(
2082        &self,
2083        secret_name: SecretName,
2084    ) -> Result<HashMap<OwnedDeviceId, OlmError>, SecretPushError> {
2085        self.inner.key_request_machine.push_secret_to_verified_devices(secret_name).await
2086    }
2087
2088    /// Get some metadata pertaining to a given group session.
2089    ///
2090    /// This includes the session owner's Matrix user ID, their device ID, info
2091    /// regarding the cryptographic algorithm and whether the session, and by
2092    /// extension the events decrypted by the session, are trusted.
2093    async fn get_encryption_info(
2094        &self,
2095        session: &InboundGroupSession,
2096        sender: &UserId,
2097    ) -> MegolmResult<Arc<EncryptionInfo>> {
2098        let (verification_state, device_id) =
2099            self.get_room_event_verification_state(session, sender).await?;
2100
2101        Ok(Arc::new(EncryptionInfo {
2102            sender: sender.to_owned(),
2103            sender_device: device_id,
2104            forwarder: session.forwarder_data.as_ref().and_then(|data| {
2105                // Per the comment on `KnownSenderData::device_id`, we should never encounter a
2106                // `None` value here, but must still deal with an `Optional` for backwards
2107                // compatibility. The approach below allows us to avoid unwrapping.
2108                data.device_id().map(|device_id| ForwarderInfo {
2109                    device_id: device_id.to_owned(),
2110                    user_id: data.user_id().to_owned(),
2111                })
2112            }),
2113            algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2114                curve25519_key: session.sender_key().to_base64(),
2115                sender_claimed_keys: session
2116                    .signing_keys()
2117                    .iter()
2118                    .map(|(k, v)| (k.to_owned(), v.to_base64()))
2119                    .collect(),
2120                session_id: Some(session.session_id().to_owned()),
2121            },
2122            verification_state,
2123        }))
2124    }
2125
2126    async fn decrypt_megolm_events(
2127        &self,
2128        room_id: &RoomId,
2129        event: &EncryptedEvent,
2130        content: &SupportedEventEncryptionSchemes<'_>,
2131        decryption_settings: &DecryptionSettings,
2132    ) -> MegolmResult<(JsonObject, Arc<EncryptionInfo>)> {
2133        let session =
2134            self.get_inbound_group_session_or_error(room_id, content.session_id()).await?;
2135
2136        // This function is only ever called by decrypt_room_event, so
2137        // room_id, sender, algorithm and session_id are recorded already
2138        //
2139        // While we already record the sender key in some cases from the event, the
2140        // sender key in the event is deprecated, so let's record it now.
2141        Span::current().record("sender_key", debug(session.sender_key()));
2142
2143        let result = session.decrypt(event).await;
2144        match result {
2145            Ok((decrypted_event, _)) => {
2146                let encryption_info = self.get_encryption_info(&session, &event.sender).await?;
2147
2148                self.check_sender_trust_requirement(
2149                    &session,
2150                    &encryption_info,
2151                    &decryption_settings.sender_device_trust_requirement,
2152                )?;
2153
2154                Ok((decrypted_event, encryption_info))
2155            }
2156            Err(error) => Err(
2157                if let MegolmError::Decryption(DecryptionError::UnknownMessageIndex(_, _)) = error {
2158                    let withheld_code = self
2159                        .inner
2160                        .store
2161                        .get_withheld_info(room_id, content.session_id())
2162                        .await?
2163                        .map(|e| e.content.withheld_code());
2164
2165                    if withheld_code.is_some() {
2166                        // Partially withheld, report with a withheld code if we have one.
2167                        MegolmError::MissingRoomKey(withheld_code)
2168                    } else {
2169                        error
2170                    }
2171                } else {
2172                    error
2173                },
2174            ),
2175        }
2176    }
2177
2178    /// Check that a Megolm event satisfies the sender trust
2179    /// requirement from the decryption settings.
2180    ///
2181    /// If the requirement is not satisfied, returns
2182    /// [`MegolmError::SenderIdentityNotTrusted`].
2183    fn check_sender_trust_requirement(
2184        &self,
2185        session: &InboundGroupSession,
2186        encryption_info: &EncryptionInfo,
2187        trust_requirement: &TrustRequirement,
2188    ) -> MegolmResult<()> {
2189        trace!(
2190            verification_state = ?encryption_info.verification_state,
2191            ?trust_requirement, "check_sender_trust_requirement",
2192        );
2193
2194        // VerificationState::Verified is acceptable for all TrustRequirement levels, so
2195        // let's get that out of the way
2196        let verification_level = match &encryption_info.verification_state {
2197            VerificationState::Verified => return Ok(()),
2198            VerificationState::Unverified(verification_level) => verification_level,
2199        };
2200
2201        let ok = match trust_requirement {
2202            TrustRequirement::Untrusted => true,
2203
2204            TrustRequirement::CrossSignedOrLegacy => {
2205                // `VerificationLevel::UnsignedDevice` and `VerificationLevel::None` correspond
2206                // to `SenderData::DeviceInfo` and `SenderData::UnknownDevice`
2207                // respectively, and those cases may be acceptable if the reason
2208                // for the lack of data is that the sessions were established
2209                // before we started collecting SenderData.
2210                let legacy_session = match session.sender_data {
2211                    SenderData::DeviceInfo { legacy_session, .. } => legacy_session,
2212                    SenderData::UnknownDevice { legacy_session, .. } => legacy_session,
2213                    _ => false,
2214                };
2215
2216                // In the CrossSignedOrLegacy case the following rules apply:
2217                //
2218                // 1. Identities we have not yet verified can be decrypted regardless of the
2219                //    legacy state of the session.
2220                // 2. Devices that aren't signed by the owning identity of the device can only
2221                //    be decrypted if it's a legacy session.
2222                // 3. If we have no information about the device, we should only decrypt if it's
2223                //    a legacy session.
2224                // 4. Anything else, should throw an error.
2225                match (verification_level, legacy_session) {
2226                    // Case 1
2227                    (VerificationLevel::UnverifiedIdentity, _) => true,
2228
2229                    // Case 2
2230                    (VerificationLevel::UnsignedDevice, true) => true,
2231
2232                    // Case 3
2233                    (VerificationLevel::None(_), true) => true,
2234
2235                    // Case 4
2236                    (VerificationLevel::VerificationViolation, _)
2237                    | (VerificationLevel::MismatchedSender, _)
2238                    | (VerificationLevel::UnsignedDevice, false)
2239                    | (VerificationLevel::None(_), false) => false,
2240                }
2241            }
2242
2243            // If cross-signing of identities is required, the only acceptable unverified case
2244            // is when the identity is signed but not yet verified by us.
2245            TrustRequirement::CrossSigned => match verification_level {
2246                VerificationLevel::UnverifiedIdentity => true,
2247
2248                VerificationLevel::VerificationViolation
2249                | VerificationLevel::MismatchedSender
2250                | VerificationLevel::UnsignedDevice
2251                | VerificationLevel::None(_) => false,
2252            },
2253        };
2254
2255        if ok {
2256            Ok(())
2257        } else {
2258            Err(MegolmError::SenderIdentityNotTrusted(verification_level.clone()))
2259        }
2260    }
2261
2262    /// Attempt to retrieve an inbound group session from the store.
2263    ///
2264    /// If the session is not found, checks for withheld reports, and returns a
2265    /// [`MegolmError::MissingRoomKey`] error.
2266    async fn get_inbound_group_session_or_error(
2267        &self,
2268        room_id: &RoomId,
2269        session_id: &str,
2270    ) -> MegolmResult<InboundGroupSession> {
2271        match self.store().get_inbound_group_session(room_id, session_id).await? {
2272            Some(session) => Ok(session),
2273            None => {
2274                let withheld_code = self
2275                    .inner
2276                    .store
2277                    .get_withheld_info(room_id, session_id)
2278                    .await?
2279                    .map(|e| e.content.withheld_code());
2280                Err(MegolmError::MissingRoomKey(withheld_code))
2281            }
2282        }
2283    }
2284
2285    /// Attempt to decrypt an event from a room timeline, returning information
2286    /// on the failure if it fails.
2287    ///
2288    /// # Arguments
2289    ///
2290    /// * `event` - The event that should be decrypted.
2291    ///
2292    /// * `room_id` - The ID of the room where the event was sent to.
2293    ///
2294    /// # Returns
2295    ///
2296    /// The decrypted event, if it was successfully decrypted. Otherwise,
2297    /// information on the failure, unless the failure was due to an
2298    /// internal error, in which case, an `Err` result.
2299    pub async fn try_decrypt_room_event(
2300        &self,
2301        raw_event: &Raw<EncryptedEvent>,
2302        room_id: &RoomId,
2303        decryption_settings: &DecryptionSettings,
2304    ) -> Result<RoomEventDecryptionResult, CryptoStoreError> {
2305        match self.decrypt_room_event_inner(raw_event, room_id, true, decryption_settings).await {
2306            Ok(decrypted) => Ok(RoomEventDecryptionResult::Decrypted(decrypted)),
2307            Err(err) => Ok(RoomEventDecryptionResult::UnableToDecrypt(megolm_error_to_utd_info(
2308                raw_event, err,
2309            )?)),
2310        }
2311    }
2312
2313    /// Decrypt an event from a room timeline.
2314    ///
2315    /// # Arguments
2316    ///
2317    /// * `event` - The event that should be decrypted.
2318    ///
2319    /// * `room_id` - The ID of the room where the event was sent to.
2320    pub async fn decrypt_room_event(
2321        &self,
2322        event: &Raw<EncryptedEvent>,
2323        room_id: &RoomId,
2324        decryption_settings: &DecryptionSettings,
2325    ) -> MegolmResult<DecryptedRoomEvent> {
2326        self.decrypt_room_event_inner(event, room_id, true, decryption_settings).await
2327    }
2328
2329    #[instrument(name = "decrypt_room_event", skip_all, fields(?room_id, event_id, origin_server_ts, sender, algorithm, session_id, message_index, sender_key))]
2330    async fn decrypt_room_event_inner(
2331        &self,
2332        event: &Raw<EncryptedEvent>,
2333        room_id: &RoomId,
2334        decrypt_unsigned: bool,
2335        decryption_settings: &DecryptionSettings,
2336    ) -> MegolmResult<DecryptedRoomEvent> {
2337        let _timer = timer!(tracing::Level::TRACE, "_method");
2338
2339        let event = event.deserialize()?;
2340
2341        Span::current()
2342            .record("sender", debug(&event.sender))
2343            .record("event_id", debug(&event.event_id))
2344            .record(
2345                "origin_server_ts",
2346                timestamp_to_iso8601(event.origin_server_ts)
2347                    .unwrap_or_else(|| "<out of range>".to_owned()),
2348            )
2349            .record("algorithm", debug(event.content.algorithm()));
2350
2351        let content: SupportedEventEncryptionSchemes<'_> = match &event.content.scheme {
2352            RoomEventEncryptionScheme::MegolmV1AesSha2(c) => {
2353                Span::current().record("sender_key", debug(c.sender_key));
2354                c.into()
2355            }
2356            #[cfg(feature = "experimental-algorithms")]
2357            RoomEventEncryptionScheme::MegolmV2AesSha2(c) => c.into(),
2358            RoomEventEncryptionScheme::Unknown(_) => {
2359                warn!("Received an encrypted room event with an unsupported algorithm");
2360                return Err(EventError::UnsupportedAlgorithm.into());
2361            }
2362        };
2363
2364        Span::current().record("session_id", content.session_id());
2365        Span::current().record("message_index", content.message_index());
2366
2367        let result =
2368            self.decrypt_megolm_events(room_id, &event, &content, decryption_settings).await;
2369
2370        if let Err(e) = &result {
2371            #[cfg(feature = "automatic-room-key-forwarding")]
2372            match e {
2373                // Optimisation should we request if we received a withheld code?
2374                // Maybe for some code there is no point
2375                MegolmError::MissingRoomKey(_)
2376                | MegolmError::Decryption(DecryptionError::UnknownMessageIndex(_, _)) => {
2377                    self.inner
2378                        .key_request_machine
2379                        .create_outgoing_key_request(room_id, &event)
2380                        .await?;
2381                }
2382                _ => {}
2383            }
2384
2385            warn!("Failed to decrypt a room event: {e}");
2386        }
2387
2388        let (mut decrypted_event, encryption_info) = result?;
2389
2390        let mut unsigned_encryption_info = None;
2391        if decrypt_unsigned {
2392            // Try to decrypt encrypted unsigned events.
2393            unsigned_encryption_info = self
2394                .decrypt_unsigned_events(&mut decrypted_event, room_id, decryption_settings)
2395                .await;
2396        }
2397
2398        let decrypted_event =
2399            serde_json::from_value::<Raw<AnyTimelineEvent>>(decrypted_event.into())?;
2400
2401        #[cfg(feature = "experimental-encrypted-state-events")]
2402        self.verify_packed_state_key(&event, &decrypted_event)?;
2403
2404        Ok(DecryptedRoomEvent { event: decrypted_event, encryption_info, unsigned_encryption_info })
2405    }
2406
2407    /// If the passed event is a state event, verify its outer packed state key
2408    /// matches the inner state key once unpacked.
2409    ///
2410    /// * `original` - The original encrypted event received over the wire.
2411    /// * `decrypted` - The decrypted event.
2412    ///
2413    /// # Errors
2414    ///
2415    /// Returns an error if any of the following are true:
2416    ///
2417    /// * The original event's state key failed to unpack;
2418    /// * The decrypted event could not be deserialised;
2419    /// * The unpacked event type does not match the type of the decrypted
2420    ///   event;
2421    /// * The unpacked event state key does not match the state key of the
2422    ///   decrypted event.
2423    #[cfg(feature = "experimental-encrypted-state-events")]
2424    fn verify_packed_state_key(
2425        &self,
2426        original: &EncryptedEvent,
2427        decrypted: &Raw<AnyTimelineEvent>,
2428    ) -> MegolmResult<()> {
2429        use serde::Deserialize;
2430
2431        // Helper for deserializing.
2432        #[derive(Deserialize)]
2433        struct PayloadDeserializationHelper {
2434            state_key: Option<String>,
2435            #[serde(rename = "type")]
2436            event_type: String,
2437        }
2438
2439        // Deserialize the decrypted event.
2440        let PayloadDeserializationHelper {
2441            state_key: inner_state_key,
2442            event_type: inner_event_type,
2443        } = decrypted
2444            .deserialize_as_unchecked()
2445            .map_err(|_| MegolmError::StateKeyVerificationFailed)?;
2446
2447        // Ensure we have a state key on the outer event iff there is one in the inner.
2448        let (raw_state_key, inner_state_key) = match (&original.state_key, &inner_state_key) {
2449            (Some(raw_state_key), Some(inner_state_key)) => (raw_state_key, inner_state_key),
2450            (None, None) => return Ok(()),
2451            _ => return Err(MegolmError::StateKeyVerificationFailed),
2452        };
2453
2454        // Unpack event type and state key from the raw state key.
2455        let (outer_event_type, outer_state_key) =
2456            raw_state_key.split_once(":").ok_or(MegolmError::StateKeyVerificationFailed)?;
2457
2458        // Check event types match, discard if not.
2459        if outer_event_type != inner_event_type {
2460            return Err(MegolmError::StateKeyVerificationFailed);
2461        }
2462
2463        // Check state keys match, discard if not.
2464        if outer_state_key != inner_state_key {
2465            return Err(MegolmError::StateKeyVerificationFailed);
2466        }
2467        Ok(())
2468    }
2469
2470    /// Try to decrypt the events bundled in the `unsigned` object of the given
2471    /// event.
2472    ///
2473    /// # Arguments
2474    ///
2475    /// * `main_event` - The event that may contain bundled encrypted events in
2476    ///   its `unsigned` object.
2477    ///
2478    /// * `room_id` - The ID of the room where the event was sent to.
2479    async fn decrypt_unsigned_events(
2480        &self,
2481        main_event: &mut JsonObject,
2482        room_id: &RoomId,
2483        decryption_settings: &DecryptionSettings,
2484    ) -> Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
2485        let unsigned = main_event.get_mut("unsigned")?.as_object_mut()?;
2486        let mut unsigned_encryption_info: Option<
2487            BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>,
2488        > = None;
2489
2490        // Search for an encrypted event in `m.replace`, an edit.
2491        let location = UnsignedEventLocation::RelationsReplace;
2492        let replace = location.find_mut(unsigned);
2493        if let Some(decryption_result) =
2494            self.decrypt_unsigned_event(replace, room_id, decryption_settings).await
2495        {
2496            unsigned_encryption_info
2497                .get_or_insert_with(Default::default)
2498                .insert(location, decryption_result);
2499        }
2500
2501        // Search for an encrypted event in `latest_event` in `m.thread`, the
2502        // latest event of a thread.
2503        let location = UnsignedEventLocation::RelationsThreadLatestEvent;
2504        let thread_latest_event = location.find_mut(unsigned);
2505        if let Some(decryption_result) =
2506            self.decrypt_unsigned_event(thread_latest_event, room_id, decryption_settings).await
2507        {
2508            unsigned_encryption_info
2509                .get_or_insert_with(Default::default)
2510                .insert(location, decryption_result);
2511        }
2512
2513        unsigned_encryption_info
2514    }
2515
2516    /// Try to decrypt the given bundled event.
2517    ///
2518    /// # Arguments
2519    ///
2520    /// * `event` - The bundled event that may be encrypted
2521    ///
2522    /// * `room_id` - The ID of the room where the event was sent to.
2523    fn decrypt_unsigned_event<'a>(
2524        &'a self,
2525        event: Option<&'a mut Value>,
2526        room_id: &'a RoomId,
2527        decryption_settings: &'a DecryptionSettings,
2528    ) -> BoxFuture<'a, Option<UnsignedDecryptionResult>> {
2529        Box::pin(async move {
2530            let event = event?;
2531
2532            let is_encrypted = event
2533                .get("type")
2534                .and_then(|type_| type_.as_str())
2535                .is_some_and(|s| s == "m.room.encrypted");
2536            if !is_encrypted {
2537                return None;
2538            }
2539
2540            let raw_event = serde_json::from_value(event.clone()).ok()?;
2541            match self
2542                .decrypt_room_event_inner(&raw_event, room_id, false, decryption_settings)
2543                .await
2544            {
2545                Ok(decrypted_event) => {
2546                    // Replace the encrypted event.
2547                    *event = serde_json::to_value(decrypted_event.event).ok()?;
2548                    Some(UnsignedDecryptionResult::Decrypted(decrypted_event.encryption_info))
2549                }
2550                Err(err) => {
2551                    // For now, we throw away crypto store errors and just treat the unsigned event
2552                    // as unencrypted. Crypto store errors represent problems with the application
2553                    // rather than normal UTD errors, so they should probably be propagated
2554                    // rather than swallowed.
2555                    let utd_info = megolm_error_to_utd_info(&raw_event, err).ok()?;
2556                    Some(UnsignedDecryptionResult::UnableToDecrypt(utd_info))
2557                }
2558            }
2559        })
2560    }
2561
2562    /// Check if we have the room key for the given event in the store.
2563    ///
2564    /// # Arguments
2565    ///
2566    /// * `event` - The event to get information for.
2567    /// * `room_id` - The ID of the room where the event was sent to.
2568    pub async fn is_room_key_available(
2569        &self,
2570        event: &Raw<EncryptedEvent>,
2571        room_id: &RoomId,
2572    ) -> Result<bool, CryptoStoreError> {
2573        let event = event.deserialize()?;
2574
2575        let (session_id, message_index) = match &event.content.scheme {
2576            RoomEventEncryptionScheme::MegolmV1AesSha2(c) => {
2577                (&c.session_id, c.ciphertext.message_index())
2578            }
2579            #[cfg(feature = "experimental-algorithms")]
2580            RoomEventEncryptionScheme::MegolmV2AesSha2(c) => {
2581                (&c.session_id, c.ciphertext.message_index())
2582            }
2583            RoomEventEncryptionScheme::Unknown(_) => {
2584                // We don't support this encryption algorithm, so clearly don't have its key.
2585                return Ok(false);
2586            }
2587        };
2588
2589        // Check that we have the session in the store, and that its first known index
2590        // predates the index of our message.
2591        Ok(self
2592            .store()
2593            .get_inbound_group_session(room_id, session_id)
2594            .await?
2595            .filter(|s| s.first_known_index() <= message_index)
2596            .is_some())
2597    }
2598
2599    /// Get encryption info for a decrypted timeline event.
2600    ///
2601    /// This recalculates the [`EncryptionInfo`] data that is returned by
2602    /// [`OlmMachine::decrypt_room_event`], based on the current
2603    /// verification status of the sender, etc.
2604    ///
2605    /// Returns an error for an unencrypted event.
2606    ///
2607    /// # Arguments
2608    ///
2609    /// * `event` - The event to get information for.
2610    /// * `room_id` - The ID of the room where the event was sent to.
2611    #[instrument(skip(self, event), fields(event_id, sender, session_id))]
2612    pub async fn get_room_event_encryption_info(
2613        &self,
2614        event: &Raw<EncryptedEvent>,
2615        room_id: &RoomId,
2616    ) -> MegolmResult<Arc<EncryptionInfo>> {
2617        let event = event.deserialize()?;
2618
2619        let content: SupportedEventEncryptionSchemes<'_> = match &event.content.scheme {
2620            RoomEventEncryptionScheme::MegolmV1AesSha2(c) => c.into(),
2621            #[cfg(feature = "experimental-algorithms")]
2622            RoomEventEncryptionScheme::MegolmV2AesSha2(c) => c.into(),
2623            RoomEventEncryptionScheme::Unknown(_) => {
2624                return Err(EventError::UnsupportedAlgorithm.into());
2625            }
2626        };
2627
2628        Span::current()
2629            .record("sender", debug(&event.sender))
2630            .record("event_id", debug(&event.event_id))
2631            .record("session_id", content.session_id());
2632
2633        self.get_session_encryption_info(room_id, content.session_id(), &event.sender).await
2634    }
2635
2636    /// Get encryption info for an event decrypted with a megolm session.
2637    ///
2638    /// This recalculates the [`EncryptionInfo`] data that is returned by
2639    /// [`OlmMachine::decrypt_room_event`], based on the current
2640    /// verification status of the sender, etc.
2641    ///
2642    /// Returns an error if the session can't be found.
2643    ///
2644    /// # Arguments
2645    ///
2646    /// * `room_id` - The ID of the room where the session is being used.
2647    /// * `session_id` - The ID of the session to get information for.
2648    /// * `sender` - The (claimed) sender of the event where the session was
2649    ///   used.
2650    pub async fn get_session_encryption_info(
2651        &self,
2652        room_id: &RoomId,
2653        session_id: &str,
2654        sender: &UserId,
2655    ) -> MegolmResult<Arc<EncryptionInfo>> {
2656        let session = self.get_inbound_group_session_or_error(room_id, session_id).await?;
2657        self.get_encryption_info(&session, sender).await
2658    }
2659
2660    /// Update the list of tracked users.
2661    ///
2662    /// The OlmMachine maintains a list of users whose devices we are keeping
2663    /// track of: these are known as "tracked users". These must be users
2664    /// that we share a room with, so that the server sends us updates for
2665    /// their device lists.
2666    ///
2667    /// # Arguments
2668    ///
2669    /// * `users` - An iterator over user ids that should be added to the list
2670    ///   of tracked users
2671    ///
2672    /// Any users that hadn't been seen before will be flagged for a key query
2673    /// immediately, and whenever [`OlmMachine::receive_sync_changes()`]
2674    /// receives a "changed" notification for that user in the future.
2675    ///
2676    /// Users that were already in the list are unaffected.
2677    pub async fn update_tracked_users(
2678        &self,
2679        users: impl IntoIterator<Item = &UserId>,
2680    ) -> StoreResult<()> {
2681        self.inner.identity_manager.update_tracked_users(users).await
2682    }
2683
2684    /// Mark all tracked users as dirty.
2685    ///
2686    /// All users *whose device lists we are tracking* are flagged as needing a
2687    /// key query. Users whose devices we are not tracking are ignored.
2688    pub async fn mark_all_tracked_users_as_dirty(&self) -> StoreResult<()> {
2689        self.inner
2690            .identity_manager
2691            .mark_all_tracked_users_as_dirty(self.inner.store.cache().await?)
2692            .await
2693    }
2694
2695    async fn wait_if_user_pending(
2696        &self,
2697        user_id: &UserId,
2698        timeout: Option<Duration>,
2699    ) -> StoreResult<()> {
2700        if let Some(timeout) = timeout {
2701            let cache = self.store().cache().await?;
2702            self.inner
2703                .identity_manager
2704                .key_query_manager
2705                .wait_if_user_key_query_pending(cache, timeout, user_id)
2706                .await?;
2707        }
2708        Ok(())
2709    }
2710
2711    /// Get a specific device of a user.
2712    ///
2713    /// # Arguments
2714    ///
2715    /// * `user_id` - The unique id of the user that the device belongs to.
2716    ///
2717    /// * `device_id` - The unique id of the device.
2718    ///
2719    /// * `timeout` - The amount of time we should wait before returning if the
2720    /// user's device list has been marked as stale. **Note**, this assumes that
2721    /// the requests from [`OlmMachine::outgoing_requests`] are being
2722    /// processed and sent out.
2723    ///
2724    /// Returns a `Device` if one is found and the crypto store didn't throw an
2725    /// error.
2726    ///
2727    /// # Examples
2728    ///
2729    /// ```
2730    /// # use matrix_sdk_crypto::OlmMachine;
2731    /// # use ruma::{device_id, owned_user_id};
2732    /// # let alice = owned_user_id!("@alice:example.org");
2733    /// # futures_executor::block_on(async {
2734    /// # let machine = OlmMachine::new(&alice, device_id!("DEVICEID")).await;
2735    /// let device = machine.get_device(&alice, device_id!("DEVICEID"), None).await;
2736    ///
2737    /// println!("{:?}", device);
2738    /// # });
2739    /// ```
2740    #[instrument(skip(self))]
2741    pub async fn get_device(
2742        &self,
2743        user_id: &UserId,
2744        device_id: &DeviceId,
2745        timeout: Option<Duration>,
2746    ) -> StoreResult<Option<Device>> {
2747        self.wait_if_user_pending(user_id, timeout).await?;
2748        self.store().get_device(user_id, device_id).await
2749    }
2750
2751    /// Get the cross signing user identity of a user.
2752    ///
2753    /// # Arguments
2754    ///
2755    /// * `user_id` - The unique id of the user that the identity belongs to
2756    ///
2757    /// * `timeout` - The amount of time we should wait before returning if the
2758    /// user's device list has been marked as stale. **Note**, this assumes that
2759    /// the requests from [`OlmMachine::outgoing_requests`] are being
2760    /// processed and sent out.
2761    ///
2762    /// Returns a [`UserIdentity`] enum if one is found and the crypto store
2763    /// didn't throw an error.
2764    #[instrument(skip(self))]
2765    pub async fn get_identity(
2766        &self,
2767        user_id: &UserId,
2768        timeout: Option<Duration>,
2769    ) -> StoreResult<Option<UserIdentity>> {
2770        self.wait_if_user_pending(user_id, timeout).await?;
2771        self.store().get_identity(user_id).await
2772    }
2773
2774    /// Get a map holding all the devices of an user.
2775    ///
2776    /// # Arguments
2777    ///
2778    /// * `user_id` - The unique id of the user that the devices belong to.
2779    ///
2780    /// * `timeout` - The amount of time we should wait before returning if the
2781    /// user's device list has been marked as stale. **Note**, this assumes that
2782    /// the requests from [`OlmMachine::outgoing_requests`] are being
2783    /// processed and sent out.
2784    ///
2785    /// # Examples
2786    ///
2787    /// ```
2788    /// # use matrix_sdk_crypto::OlmMachine;
2789    /// # use ruma::{device_id, owned_user_id};
2790    /// # let alice = owned_user_id!("@alice:example.org");
2791    /// # futures_executor::block_on(async {
2792    /// # let machine = OlmMachine::new(&alice, device_id!("DEVICEID")).await;
2793    /// let devices = machine.get_user_devices(&alice, None).await.unwrap();
2794    ///
2795    /// for device in devices.devices() {
2796    ///     println!("{:?}", device);
2797    /// }
2798    /// # });
2799    /// ```
2800    #[instrument(skip(self))]
2801    pub async fn get_user_devices(
2802        &self,
2803        user_id: &UserId,
2804        timeout: Option<Duration>,
2805    ) -> StoreResult<UserDevices> {
2806        self.wait_if_user_pending(user_id, timeout).await?;
2807        self.store().get_user_devices(user_id).await
2808    }
2809
2810    /// Get the status of the private cross signing keys.
2811    ///
2812    /// This can be used to check which private cross signing keys we have
2813    /// stored locally.
2814    pub async fn cross_signing_status(&self) -> CrossSigningStatus {
2815        self.inner.user_identity.lock().await.status().await
2816    }
2817
2818    /// Export all the private cross signing keys we have.
2819    ///
2820    /// The export will contain the seed for the ed25519 keys as a unpadded
2821    /// base64 encoded string.
2822    ///
2823    /// This method returns `None` if we don't have any private cross signing
2824    /// keys.
2825    pub async fn export_cross_signing_keys(&self) -> StoreResult<Option<CrossSigningKeyExport>> {
2826        let master_key = self.store().export_secret(&SecretName::CrossSigningMasterKey).await?;
2827        let self_signing_key =
2828            self.store().export_secret(&SecretName::CrossSigningSelfSigningKey).await?;
2829        let user_signing_key =
2830            self.store().export_secret(&SecretName::CrossSigningUserSigningKey).await?;
2831
2832        Ok(if master_key.is_none() && self_signing_key.is_none() && user_signing_key.is_none() {
2833            None
2834        } else {
2835            Some(CrossSigningKeyExport { master_key, self_signing_key, user_signing_key })
2836        })
2837    }
2838
2839    /// Import our private cross signing keys.
2840    ///
2841    /// The export needs to contain the seed for the ed25519 keys as an unpadded
2842    /// base64 encoded string.
2843    pub async fn import_cross_signing_keys(
2844        &self,
2845        export: CrossSigningKeyExport,
2846    ) -> Result<CrossSigningStatus, SecretImportError> {
2847        self.store().import_cross_signing_keys(export).await
2848    }
2849
2850    async fn sign_with_master_key(
2851        &self,
2852        message: &str,
2853    ) -> Result<(OwnedDeviceKeyId, Ed25519Signature), SignatureError> {
2854        let identity = &*self.inner.user_identity.lock().await;
2855        let key_id = identity.master_key_id().await.ok_or(SignatureError::MissingSigningKey)?;
2856
2857        let signature = identity.sign(message).await?;
2858
2859        Ok((key_id, signature))
2860    }
2861
2862    /// Sign the given message using our device key and if available cross
2863    /// signing master key.
2864    ///
2865    /// Presently, this should only be used for signing the server-side room
2866    /// key backups.
2867    pub async fn sign(&self, message: &str) -> Result<Signatures, CryptoStoreError> {
2868        let mut signatures = Signatures::new();
2869
2870        {
2871            let cache = self.inner.store.cache().await?;
2872            let account = cache.account().await?;
2873            let key_id = account.signing_key_id();
2874            let signature = account.sign(message);
2875            signatures.add_signature(self.user_id().to_owned(), key_id, signature);
2876        }
2877
2878        match self.sign_with_master_key(message).await {
2879            Ok((key_id, signature)) => {
2880                signatures.add_signature(self.user_id().to_owned(), key_id, signature);
2881            }
2882            Err(e) => {
2883                warn!(error = ?e, "Couldn't sign the message using the cross signing master key")
2884            }
2885        }
2886
2887        Ok(signatures)
2888    }
2889
2890    /// Get a reference to the backup related state machine.
2891    ///
2892    /// This state machine can be used to incrementally backup all room keys to
2893    /// the server.
2894    pub fn backup_machine(&self) -> &BackupMachine {
2895        &self.inner.backup_machine
2896    }
2897
2898    /// Syncs the database and in-memory generation counter.
2899    ///
2900    /// This requires that the crypto store lock has been acquired already.
2901    pub async fn initialize_crypto_store_generation(
2902        &self,
2903        generation: &Mutex<Option<u64>>,
2904    ) -> StoreResult<()> {
2905        // Avoid reentrant initialization by taking the lock for the entire's function
2906        // scope.
2907        let mut gen_guard = generation.lock().await;
2908
2909        let prev_generation =
2910            self.inner.store.get_custom_value(Self::CURRENT_GENERATION_STORE_KEY).await?;
2911
2912        let generation = match prev_generation {
2913            Some(val) => {
2914                // There was a value in the store. We need to signal that we're a different
2915                // process, so we don't just reuse the value but increment it.
2916                u64::from_le_bytes(val.try_into().map_err(|_| {
2917                    CryptoStoreError::InvalidLockGeneration("invalid format".to_owned())
2918                })?)
2919                .wrapping_add(1)
2920            }
2921            None => 0,
2922        };
2923
2924        tracing::debug!("Initialising crypto store generation at {generation}");
2925
2926        self.inner
2927            .store
2928            .set_custom_value(Self::CURRENT_GENERATION_STORE_KEY, generation.to_le_bytes().to_vec())
2929            .await?;
2930
2931        *gen_guard = Some(generation);
2932
2933        Ok(())
2934    }
2935
2936    /// If needs be, update the local and on-disk crypto store generation.
2937    ///
2938    /// ## Requirements
2939    ///
2940    /// - This assumes that `initialize_crypto_store_generation` has been called
2941    ///   beforehand.
2942    /// - This requires that the crypto store lock has been acquired.
2943    ///
2944    /// # Arguments
2945    ///
2946    /// * `generation` - The in-memory generation counter (or rather, the
2947    ///   `Mutex` wrapping it). This defines the "expected" generation on entry,
2948    ///   and, if we determine an update is needed, is updated to hold the "new"
2949    ///   generation.
2950    ///
2951    /// # Returns
2952    ///
2953    /// A tuple containing:
2954    ///
2955    /// * A `bool`, set to `true` if another process has updated the generation
2956    ///   number in the `Store` since our expected value, and as such we've
2957    ///   incremented and updated it in the database. Otherwise, `false`.
2958    ///
2959    /// * The (possibly updated) generation counter.
2960    pub async fn maintain_crypto_store_generation(
2961        &'_ self,
2962        generation: &Mutex<Option<u64>>,
2963    ) -> StoreResult<(bool, u64)> {
2964        let mut gen_guard = generation.lock().await;
2965
2966        // The database value must be there:
2967        // - either we could initialize beforehand, thus write into the database,
2968        // - or we couldn't, and then another process was holding onto the database's
2969        //   lock, thus
2970        // has written a generation counter in there.
2971        let actual_gen = self
2972            .inner
2973            .store
2974            .get_custom_value(Self::CURRENT_GENERATION_STORE_KEY)
2975            .await?
2976            .ok_or_else(|| {
2977                CryptoStoreError::InvalidLockGeneration("counter missing in store".to_owned())
2978            })?;
2979
2980        let actual_gen =
2981            u64::from_le_bytes(actual_gen.try_into().map_err(|_| {
2982                CryptoStoreError::InvalidLockGeneration("invalid format".to_owned())
2983            })?);
2984
2985        let new_gen = match gen_guard.as_ref() {
2986            Some(expected_gen) => {
2987                if actual_gen == *expected_gen {
2988                    return Ok((false, actual_gen));
2989                }
2990                // Increment the biggest, and store it everywhere.
2991                actual_gen.max(*expected_gen).wrapping_add(1)
2992            }
2993            None => {
2994                // Some other process hold onto the lock when initializing, so we must reload.
2995                // Increment database value, and store it everywhere.
2996                actual_gen.wrapping_add(1)
2997            }
2998        };
2999
3000        tracing::debug!(
3001            "Crypto store generation mismatch: previously known was {:?}, actual is {:?}, next is {}",
3002            *gen_guard,
3003            actual_gen,
3004            new_gen
3005        );
3006
3007        // Update known value.
3008        *gen_guard = Some(new_gen);
3009
3010        // Update value in database.
3011        self.inner
3012            .store
3013            .set_custom_value(Self::CURRENT_GENERATION_STORE_KEY, new_gen.to_le_bytes().to_vec())
3014            .await?;
3015
3016        Ok((true, new_gen))
3017    }
3018
3019    /// Manage dehydrated devices.
3020    pub fn dehydrated_devices(&self) -> DehydratedDevices {
3021        DehydratedDevices { inner: self.to_owned() }
3022    }
3023
3024    /// Get the stored encryption settings for the given room, such as the
3025    /// encryption algorithm or whether to encrypt only for trusted devices.
3026    ///
3027    /// These settings can be modified via [`OlmMachine::set_room_settings`].
3028    pub async fn room_settings(&self, room_id: &RoomId) -> StoreResult<Option<RoomSettings>> {
3029        // There's not much to do here: it's just exposed for symmetry with
3030        // `set_room_settings`.
3031        self.inner.store.get_room_settings(room_id).await
3032    }
3033
3034    /// Store encryption settings for the given room.
3035    ///
3036    /// This method checks if the new settings are "safe" -- ie, that they do
3037    /// not represent a downgrade in encryption security from any previous
3038    /// settings. Attempts to downgrade security will result in a
3039    /// [`SetRoomSettingsError::EncryptionDowngrade`].
3040    ///
3041    /// If the settings are valid, they will be persisted to the crypto store.
3042    /// These settings are not used directly by this library, but the saved
3043    /// settings can be retrieved via [`OlmMachine::room_settings`].
3044    pub async fn set_room_settings(
3045        &self,
3046        room_id: &RoomId,
3047        new_settings: &RoomSettings,
3048    ) -> Result<(), SetRoomSettingsError> {
3049        let store = &self.inner.store;
3050
3051        // We want to make sure that we do not race against a second concurrent call to
3052        // `set_room_settings`. By way of an easy way to do so, we start a
3053        // StoreTransaction. There's no need to commit() it: we're just using it as a
3054        // lock guard.
3055        let _store_transaction = store.transaction().await;
3056
3057        let old_settings = store.get_room_settings(room_id).await?;
3058
3059        // We want to make sure that the change to the room settings does not represent
3060        // a downgrade in security. The [E2EE implementation guide] recommends:
3061        //
3062        //  > This flag should **not** be cleared if a later `m.room.encryption` event
3063        //  > changes the configuration.
3064        //
3065        // (However, it doesn't really address how to handle changes to the rotation
3066        // parameters, etc.) For now at least, we are very conservative here:
3067        // any new settings are rejected if they differ from the existing settings.
3068        // merit improvement (cf https://github.com/element-hq/element-meta/issues/69).
3069        //
3070        // [E2EE implementation guide]: https://matrix.org/docs/matrix-concepts/end-to-end-encryption/#handling-an-m-room-encryption-state-event
3071        if let Some(old_settings) = old_settings {
3072            if old_settings != *new_settings {
3073                return Err(SetRoomSettingsError::EncryptionDowngrade);
3074            } else {
3075                // nothing to do here
3076                return Ok(());
3077            }
3078        }
3079
3080        // Make sure that the new settings are valid
3081        match new_settings.algorithm {
3082            EventEncryptionAlgorithm::MegolmV1AesSha2 => (),
3083
3084            #[cfg(feature = "experimental-algorithms")]
3085            EventEncryptionAlgorithm::MegolmV2AesSha2 => (),
3086
3087            _ => {
3088                warn!(
3089                    ?room_id,
3090                    "Rejecting invalid encryption algorithm {}", new_settings.algorithm
3091                );
3092                return Err(SetRoomSettingsError::InvalidSettings);
3093            }
3094        }
3095
3096        // The new settings are acceptable, so let's save them.
3097        store
3098            .save_changes(Changes {
3099                room_settings: HashMap::from([(room_id.to_owned(), new_settings.clone())]),
3100                ..Default::default()
3101            })
3102            .await?;
3103
3104        Ok(())
3105    }
3106
3107    /// Returns whether this `OlmMachine` is the same another one.
3108    ///
3109    /// Useful for testing purposes only.
3110    #[cfg(any(feature = "testing", test))]
3111    pub fn same_as(&self, other: &OlmMachine) -> bool {
3112        Arc::ptr_eq(&self.inner, &other.inner)
3113    }
3114
3115    /// Testing purposes only.
3116    #[cfg(any(feature = "testing", test))]
3117    pub async fn uploaded_key_count(&self) -> Result<u64, CryptoStoreError> {
3118        let cache = self.inner.store.cache().await?;
3119        let account = cache.account().await?;
3120        Ok(account.uploaded_key_count())
3121    }
3122
3123    /// Returns the identity manager.
3124    #[cfg(test)]
3125    pub(crate) fn identity_manager(&self) -> &IdentityManager {
3126        &self.inner.identity_manager
3127    }
3128
3129    /// Returns a store key, only useful for testing purposes.
3130    #[cfg(test)]
3131    pub(crate) fn key_for_has_migrated_verification_latch() -> &'static str {
3132        Self::HAS_MIGRATED_VERIFICATION_LATCH
3133    }
3134}
3135
3136fn sender_data_to_verification_state(
3137    sender_data: SenderData,
3138    session_has_been_imported: bool,
3139) -> (VerificationState, Option<OwnedDeviceId>) {
3140    match sender_data {
3141        SenderData::UnknownDevice { owner_check_failed: false, .. } => {
3142            let device_link_problem = if session_has_been_imported {
3143                DeviceLinkProblem::InsecureSource
3144            } else {
3145                DeviceLinkProblem::MissingDevice
3146            };
3147
3148            (VerificationState::Unverified(VerificationLevel::None(device_link_problem)), None)
3149        }
3150        SenderData::UnknownDevice { owner_check_failed: true, .. } => (
3151            VerificationState::Unverified(VerificationLevel::None(
3152                DeviceLinkProblem::InsecureSource,
3153            )),
3154            None,
3155        ),
3156        SenderData::DeviceInfo { device_keys, .. } => (
3157            VerificationState::Unverified(VerificationLevel::UnsignedDevice),
3158            Some(device_keys.device_id),
3159        ),
3160        SenderData::VerificationViolation(KnownSenderData { device_id, .. }) => {
3161            (VerificationState::Unverified(VerificationLevel::VerificationViolation), device_id)
3162        }
3163        SenderData::SenderUnverified(KnownSenderData { device_id, .. }) => {
3164            (VerificationState::Unverified(VerificationLevel::UnverifiedIdentity), device_id)
3165        }
3166        SenderData::SenderVerified(KnownSenderData { device_id, .. }) => {
3167            (VerificationState::Verified, device_id)
3168        }
3169    }
3170}
3171
3172/// A set of requests to be executed when bootstrapping cross-signing using
3173/// [`OlmMachine::bootstrap_cross_signing`].
3174#[derive(Debug, Clone)]
3175pub struct CrossSigningBootstrapRequests {
3176    /// An optional request to upload a device key.
3177    ///
3178    /// Should be sent first, if present.
3179    ///
3180    /// If present, its result must be processed back with
3181    /// `OlmMachine::mark_request_as_sent`.
3182    pub upload_keys_req: Option<OutgoingRequest>,
3183
3184    /// Request to upload the cross-signing keys.
3185    ///
3186    /// Should be sent second.
3187    pub upload_signing_keys_req: UploadSigningKeysRequest,
3188
3189    /// Request to upload key signatures, including those for the cross-signing
3190    /// keys, and maybe some for the optional uploaded key too.
3191    ///
3192    /// Should be sent last.
3193    pub upload_signatures_req: UploadSignaturesRequest,
3194}
3195
3196/// Data contained from a sync response and that needs to be processed by the
3197/// OlmMachine.
3198#[derive(Debug)]
3199pub struct EncryptionSyncChanges<'a> {
3200    /// The list of to-device events received in the sync.
3201    pub to_device_events: Vec<Raw<AnyToDeviceEvent>>,
3202    /// The mapping of changed and left devices, per user, as returned in the
3203    /// sync response.
3204    pub changed_devices: &'a DeviceLists,
3205    /// The number of one time keys, as returned in the sync response.
3206    pub one_time_keys_counts: &'a BTreeMap<OneTimeKeyAlgorithm, UInt>,
3207    /// An optional list of fallback keys.
3208    pub unused_fallback_keys: Option<&'a [OneTimeKeyAlgorithm]>,
3209    /// A next-batch token obtained from a to-device sync query.
3210    pub next_batch_token: Option<String>,
3211}
3212
3213/// Convert a [`MegolmError`] into an [`UnableToDecryptInfo`] or a
3214/// [`CryptoStoreError`].
3215///
3216/// Most `MegolmError` codes are converted into a suitable
3217/// `UnableToDecryptInfo`. The exception is [`MegolmError::Store`], which
3218/// represents a problem with our datastore rather than with the message itself,
3219/// and is therefore returned as a `CryptoStoreError`.
3220fn megolm_error_to_utd_info(
3221    raw_event: &Raw<EncryptedEvent>,
3222    error: MegolmError,
3223) -> Result<UnableToDecryptInfo, CryptoStoreError> {
3224    use MegolmError::*;
3225    let reason = match error {
3226        EventError(_) => UnableToDecryptReason::MalformedEncryptedEvent,
3227        Decode(_) => UnableToDecryptReason::MalformedEncryptedEvent,
3228        MissingRoomKey(maybe_withheld) => {
3229            UnableToDecryptReason::MissingMegolmSession { withheld_code: maybe_withheld }
3230        }
3231        Decryption(DecryptionError::UnknownMessageIndex(_, _)) => {
3232            UnableToDecryptReason::UnknownMegolmMessageIndex
3233        }
3234        Decryption(_) => UnableToDecryptReason::MegolmDecryptionFailure,
3235        JsonError(_) => UnableToDecryptReason::PayloadDeserializationFailure,
3236        MismatchedIdentityKeys(_) => UnableToDecryptReason::MismatchedIdentityKeys,
3237        SenderIdentityNotTrusted(level) => UnableToDecryptReason::SenderIdentityNotTrusted(level),
3238        #[cfg(feature = "experimental-encrypted-state-events")]
3239        StateKeyVerificationFailed => UnableToDecryptReason::StateKeyVerificationFailed,
3240
3241        // Pass through crypto store errors, which indicate a problem with our
3242        // application, rather than a UTD.
3243        Store(error) => Err(error)?,
3244    };
3245
3246    let session_id = raw_event.deserialize().ok().and_then(|ev| match ev.content.scheme {
3247        RoomEventEncryptionScheme::MegolmV1AesSha2(s) => Some(s.session_id),
3248        #[cfg(feature = "experimental-algorithms")]
3249        RoomEventEncryptionScheme::MegolmV2AesSha2(s) => Some(s.session_id),
3250        RoomEventEncryptionScheme::Unknown(_) => None,
3251    });
3252
3253    Ok(UnableToDecryptInfo { session_id, reason })
3254}
3255
3256/// An error that can occur during [`OlmMachine::decrypt_to_device_event`]:
3257///
3258/// * because decryption failed, or
3259///
3260/// * because the sender device was not verified when we are in strict "exclude
3261///   insecure devices" mode, or
3262///
3263/// * because the sender device was a dehydrated device, which should never send
3264///   any to-device messages.
3265#[derive(Debug, thiserror::Error)]
3266pub(crate) enum DecryptToDeviceError {
3267    #[error("An Olm error occurred meaning we failed to decrypt the event")]
3268    OlmError(#[from] OlmError),
3269
3270    #[error("The event was sent from a dehydrated device")]
3271    FromDehydratedDevice,
3272}
3273
3274impl From<CryptoStoreError> for DecryptToDeviceError {
3275    fn from(value: CryptoStoreError) -> Self {
3276        Self::OlmError(value.into())
3277    }
3278}
3279
3280#[cfg(test)]
3281impl From<DecryptToDeviceError> for OlmError {
3282    /// Unwrap the `OlmError` inside this error, or panic if this does not
3283    /// contain an `OlmError`.
3284    fn from(value: DecryptToDeviceError) -> Self {
3285        match value {
3286            DecryptToDeviceError::OlmError(olm_error) => olm_error,
3287            DecryptToDeviceError::FromDehydratedDevice => {
3288                panic!("Expected an OlmError but found FromDehydratedDevice")
3289            }
3290        }
3291    }
3292}
3293
3294#[cfg(test)]
3295pub(crate) mod test_helpers;
3296
3297#[cfg(test)]
3298pub(crate) mod tests;