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