matrix_sdk_crypto/machine/
mod.rs

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