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