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