Skip to main content

matrix_sdk_crypto/machine/
mod.rs

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