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