Skip to main content

matrix_sdk_crypto_ffi/
machine.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    io::Cursor,
4    mem::ManuallyDrop,
5    ops::Deref,
6    sync::Arc,
7    time::Duration,
8};
9
10use js_int::UInt;
11use matrix_sdk_common::deserialized_responses::AlgorithmInfo;
12use matrix_sdk_crypto::{
13    CollectStrategy, DecryptionSettings, LocalTrust, OlmMachine as InnerMachine, OlmMachineBuilder,
14    UserIdentity as SdkUserIdentity,
15    backups::{
16        MegolmV1BackupKey as RustBackupKey, SignatureState,
17        SignatureVerification as RustSignatureCheckResult,
18    },
19    decrypt_room_key_export, encrypt_room_key_export,
20    olm::ExportedRoomKey,
21    store::types::{BackupDecryptionKey, Changes},
22    types::requests::ToDeviceRequest,
23};
24use ruma::{
25    DeviceKeyAlgorithm, EventId, OneTimeKeyAlgorithm, OwnedTransactionId, OwnedUserId, RoomId,
26    UserId,
27    api::{
28        IncomingResponse,
29        client::{
30            backup::add_backup_keys::v3::Response as KeysBackupResponse,
31            keys::{
32                claim_keys::v3::Response as KeysClaimResponse,
33                get_keys::v3::Response as KeysQueryResponse,
34                upload_keys::v3::Response as KeysUploadResponse,
35                upload_signatures::v3::Response as SignatureUploadResponse,
36            },
37            message::send_message_event::v3::Response as RoomMessageResponse,
38            sync::sync_events::{DeviceLists as RumaDeviceLists, v3::ToDevice},
39            to_device::send_event_to_device::v3::Response as ToDeviceResponse,
40        },
41    },
42    events::{
43        AnyMessageLikeEvent, AnySyncMessageLikeEvent, AnyTimelineEvent, MessageLikeEvent,
44        key::verification::VerificationMethod, room::message::MessageType,
45    },
46    serde::Raw,
47    to_device::DeviceIdOrAllDevices,
48};
49use serde::{Deserialize, Serialize};
50use serde_json::{Value, value::RawValue};
51use tokio::runtime::Runtime;
52use zeroize::Zeroize;
53
54use crate::{
55    BackupKeys, BackupRecoveryKey, BootstrapCrossSigningError, BootstrapCrossSigningResult,
56    CrossSigningKeyExport, CrossSigningStatus, DecodeError, DecryptedEvent, Device, DeviceLists,
57    EncryptionSettings, EventEncryptionAlgorithm, KeyImportError, KeysImportResult,
58    MegolmV1BackupKey, ProgressListener, Request, RequestType, RequestVerificationResult,
59    RoomKeyCounts, RoomSettings, Sas, SignatureUploadRequest, StartSasResult, UserIdentity,
60    Verification, VerificationRequest,
61    dehydrated_devices::DehydratedDevices,
62    error::{
63        CryptoStoreError, DecryptionError, SecretImportError, SecretsBundleExportError,
64        SignatureError,
65    },
66    parse_user_id,
67    responses::{OwnedResponse, response_from_string},
68};
69
70/// The return value for the [`OlmMachine::receive_sync_changes()`] method.
71///
72/// Will contain various information about the `/sync` changes the
73/// [`OlmMachine`] processed.
74#[derive(uniffi::Record)]
75pub struct SyncChangesResult {
76    /// The, now possibly decrypted, to-device events the [`OlmMachine`]
77    /// received, decrypted, and processed.
78    to_device_events: Vec<String>,
79
80    /// Information about the room keys that were extracted out of the to-device
81    /// events.
82    room_key_infos: Vec<RoomKeyInfo>,
83}
84
85/// Information on a room key that has been received or imported.
86#[derive(uniffi::Record)]
87pub struct RoomKeyInfo {
88    /// The [messaging algorithm] that this key is used for. Will be one of the
89    /// `m.megolm.*` algorithms.
90    ///
91    /// [messaging algorithm]: https://spec.matrix.org/v1.6/client-server-api/#messaging-algorithms
92    pub algorithm: String,
93
94    /// The room where the key is used.
95    pub room_id: String,
96
97    /// The Curve25519 key of the device which initiated the session originally.
98    pub sender_key: String,
99
100    /// The ID of the session that the key is for.
101    pub session_id: String,
102}
103
104impl From<matrix_sdk_crypto::store::types::RoomKeyInfo> for RoomKeyInfo {
105    fn from(value: matrix_sdk_crypto::store::types::RoomKeyInfo) -> Self {
106        Self {
107            algorithm: value.algorithm.to_string(),
108            room_id: value.room_id.to_string(),
109            sender_key: value.sender_key.to_base64(),
110            session_id: value.session_id,
111        }
112    }
113}
114
115/// A high level state machine that handles E2EE for Matrix.
116#[derive(uniffi::Object)]
117pub struct OlmMachine {
118    pub(crate) inner: ManuallyDrop<InnerMachine>,
119    pub(crate) runtime: Runtime,
120}
121
122impl Drop for OlmMachine {
123    fn drop(&mut self) {
124        // Dropping the inner OlmMachine must happen within a tokio context
125        // because deadpool drops sqlite connections in the DB pool on tokio's
126        // blocking threadpool to avoid blocking async worker threads.
127        let _guard = self.runtime.enter();
128        // SAFETY: self.inner is never used again, which is the only requirement
129        //         for ManuallyDrop::drop to be used safely.
130        unsafe {
131            ManuallyDrop::drop(&mut self.inner);
132        }
133    }
134}
135
136/// A pair of outgoing room key requests, both of those are sendToDevice
137/// requests.
138#[derive(uniffi::Record)]
139pub struct KeyRequestPair {
140    /// The optional cancellation, this is None if no previous key request was
141    /// sent out for this key, thus it doesn't need to be cancelled.
142    pub cancellation: Option<Request>,
143    /// The actual key request.
144    pub key_request: Request,
145}
146
147/// The result of a signature verification of a signed JSON object.
148#[derive(Clone, Debug, PartialEq, Eq, uniffi::Record)]
149pub struct SignatureVerification {
150    /// The result of the signature verification using the public key of our own
151    /// device.
152    pub device_signature: SignatureState,
153    /// The result of the signature verification using the public key of our own
154    /// user identity.
155    pub user_identity_signature: SignatureState,
156    /// The result of the signature verification using public keys of other
157    /// devices we own.
158    pub other_devices_signatures: HashMap<String, SignatureState>,
159    /// Is the signed JSON object trusted.
160    ///
161    /// This flag tells us if the result has a valid signature from any of the
162    /// following:
163    ///
164    /// * Our own device
165    /// * Our own user identity, provided the identity is trusted as well
166    /// * Any of our own devices, provided the device is trusted as well
167    pub trusted: bool,
168}
169
170impl From<RustSignatureCheckResult> for SignatureVerification {
171    fn from(r: RustSignatureCheckResult) -> Self {
172        let trusted = r.trusted();
173
174        Self {
175            device_signature: r.device_signature,
176            user_identity_signature: r.user_identity_signature,
177            other_devices_signatures: r
178                .other_signatures
179                .into_iter()
180                .map(|(k, v)| (k.to_string(), v))
181                .collect(),
182            trusted,
183        }
184    }
185}
186
187#[matrix_sdk_ffi_macros::export]
188impl OlmMachine {
189    /// Create a new `OlmMachine`
190    ///
191    /// # Arguments
192    ///
193    /// * `user_id` - The unique ID of the user that owns this machine.
194    ///
195    /// * `device_id` - The unique ID of the device that owns this machine.
196    ///
197    /// * `path` - The path where the state of the machine should be persisted.
198    ///
199    /// * `passphrase` - The passphrase that should be used to encrypt the data
200    ///   at rest in the crypto store. **Warning**, if no passphrase is given,
201    ///   the store and all its data will remain unencrypted.
202    #[uniffi::constructor]
203    pub fn new(
204        user_id: String,
205        device_id: String,
206        path: String,
207        mut passphrase: Option<String>,
208    ) -> Result<Arc<Self>, CryptoStoreError> {
209        let user_id = parse_user_id(&user_id)?;
210        let device_id = device_id.as_str().into();
211        let runtime = Runtime::new().expect("Couldn't create a tokio runtime");
212
213        let store = runtime
214            .block_on(matrix_sdk_sqlite::SqliteCryptoStore::open(path, passphrase.as_deref()))?;
215
216        passphrase.zeroize();
217
218        let inner = runtime.block_on(
219            OlmMachineBuilder::new(&user_id, device_id).with_crypto_store(Arc::new(store)).build(),
220        )?;
221
222        Ok(Arc::new(OlmMachine { inner: ManuallyDrop::new(inner), runtime }))
223    }
224
225    /// Get the user ID of the owner of this `OlmMachine`.
226    pub fn user_id(&self) -> String {
227        self.inner.user_id().to_string()
228    }
229
230    /// Get the device ID of the device of this `OlmMachine`.
231    pub fn device_id(&self) -> String {
232        self.inner.device_id().to_string()
233    }
234
235    /// Get our own identity keys.
236    pub fn identity_keys(&self) -> HashMap<String, String> {
237        let identity_keys = self.inner.identity_keys();
238        let curve_key = identity_keys.curve25519.to_base64();
239        let ed25519_key = identity_keys.ed25519.to_base64();
240
241        HashMap::from([("ed25519".to_owned(), ed25519_key), ("curve25519".to_owned(), curve_key)])
242    }
243
244    /// Get the status of the private cross signing keys.
245    ///
246    /// This can be used to check which private cross signing keys we have
247    /// stored locally.
248    pub fn cross_signing_status(&self) -> CrossSigningStatus {
249        self.runtime.block_on(self.inner.cross_signing_status()).into()
250    }
251
252    /// Get a cross signing user identity for the given user ID.
253    ///
254    /// # Arguments
255    ///
256    /// * `user_id` - The unique id of the user that the identity belongs to
257    ///
258    /// * `timeout` - The time in seconds we should wait before returning if the
259    ///   user's device list has been marked as stale. Passing a 0 as the
260    ///   timeout means that we won't wait at all. **Note**, this assumes that
261    ///   the requests from [`OlmMachine::outgoing_requests`] are being
262    ///   processed and sent out. Namely, this waits for a `/keys/query`
263    ///   response to be received.
264    pub fn get_identity(
265        &self,
266        user_id: String,
267        timeout: u32,
268    ) -> Result<Option<UserIdentity>, CryptoStoreError> {
269        let user_id = parse_user_id(&user_id)?;
270
271        let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) };
272
273        Ok(
274            if let Some(identity) =
275                self.runtime.block_on(self.inner.get_identity(&user_id, timeout))?
276            {
277                Some(self.runtime.block_on(UserIdentity::from_rust(identity))?)
278            } else {
279                None
280            },
281        )
282    }
283
284    /// Check if a user identity is considered to be verified by us.
285    pub fn is_identity_verified(&self, user_id: String) -> Result<bool, CryptoStoreError> {
286        let user_id = parse_user_id(&user_id)?;
287
288        Ok(
289            if let Some(identity) =
290                self.runtime.block_on(self.inner.get_identity(&user_id, None))?
291            {
292                identity.is_verified()
293            } else {
294                false
295            },
296        )
297    }
298
299    /// Manually the user with the given user ID.
300    ///
301    /// This method will attempt to sign the user identity using either our
302    /// private cross signing key, for other user identities, or our device keys
303    /// for our own user identity.
304    ///
305    /// This method can fail if we don't have the private part of our
306    /// user-signing key.
307    ///
308    /// Returns a request that needs to be sent out for the user identity to be
309    /// marked as verified.
310    pub fn verify_identity(
311        &self,
312        user_id: String,
313    ) -> Result<SignatureUploadRequest, SignatureError> {
314        let user_id = UserId::parse(user_id)?;
315
316        let user_identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?;
317
318        if let Some(user_identity) = user_identity {
319            Ok(match user_identity {
320                SdkUserIdentity::Own(i) => self.runtime.block_on(i.verify())?,
321                SdkUserIdentity::Other(i) => self.runtime.block_on(i.verify())?,
322            }
323            .into())
324        } else {
325            Err(SignatureError::UnknownUserIdentity(user_id.to_string()))
326        }
327    }
328
329    /// Get a `Device` from the store.
330    ///
331    /// # Arguments
332    ///
333    /// * `user_id` - The id of the device owner.
334    ///
335    /// * `device_id` - The id of the device itself.
336    ///
337    /// * `timeout` - The time in seconds we should wait before returning if the
338    ///   user's device list has been marked as stale. Passing a 0 as the
339    ///   timeout means that we won't wait at all. **Note**, this assumes that
340    ///   the requests from [`OlmMachine::outgoing_requests`] are being
341    ///   processed and sent out. Namely, this waits for a `/keys/query`
342    ///   response to be received.
343    pub fn get_device(
344        &self,
345        user_id: String,
346        device_id: String,
347        timeout: u32,
348    ) -> Result<Option<Device>, CryptoStoreError> {
349        let user_id = parse_user_id(&user_id)?;
350
351        let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) };
352
353        Ok(self
354            .runtime
355            .block_on(self.inner.get_device(&user_id, device_id.as_str().into(), timeout))?
356            .map(|d| d.into()))
357    }
358
359    /// Manually verify the device of the given user with the given device ID.
360    ///
361    /// This method will attempt to sign the device using our private cross
362    /// signing key.
363    ///
364    /// This method will always fail if the device belongs to someone else, we
365    /// can only sign our own devices.
366    ///
367    /// It can also fail if we don't have the private part of our self-signing
368    /// key.
369    ///
370    /// Returns a request that needs to be sent out for the device to be marked
371    /// as verified.
372    pub fn verify_device(
373        &self,
374        user_id: String,
375        device_id: String,
376    ) -> Result<SignatureUploadRequest, SignatureError> {
377        let user_id = UserId::parse(user_id)?;
378        let device = self.runtime.block_on(self.inner.get_device(
379            &user_id,
380            device_id.as_str().into(),
381            None,
382        ))?;
383
384        if let Some(device) = device {
385            Ok(self.runtime.block_on(device.verify())?.into())
386        } else {
387            Err(SignatureError::UnknownDevice(user_id, device_id))
388        }
389    }
390
391    /// Set local trust state for the device of the given user without creating
392    /// or uploading any signatures if verified
393    pub fn set_local_trust(
394        &self,
395        user_id: String,
396        device_id: String,
397        trust_state: LocalTrust,
398    ) -> Result<(), CryptoStoreError> {
399        let user_id = parse_user_id(&user_id)?;
400
401        let device = self.runtime.block_on(self.inner.get_device(
402            &user_id,
403            device_id.as_str().into(),
404            None,
405        ))?;
406
407        if let Some(device) = device {
408            self.runtime.block_on(device.set_local_trust(trust_state))?;
409        }
410
411        Ok(())
412    }
413
414    /// Get all devices of an user.
415    ///
416    /// # Arguments
417    ///
418    /// * `user_id` - The id of the device owner.
419    ///
420    /// * `timeout` - The time in seconds we should wait before returning if the
421    ///   user's device list has been marked as stale. Passing a 0 as the
422    ///   timeout means that we won't wait at all. **Note**, this assumes that
423    ///   the requests from [`OlmMachine::outgoing_requests`] are being
424    ///   processed and sent out. Namely, this waits for a `/keys/query`
425    ///   response to be received.
426    pub fn get_user_devices(
427        &self,
428        user_id: String,
429        timeout: u32,
430    ) -> Result<Vec<Device>, CryptoStoreError> {
431        let user_id = parse_user_id(&user_id)?;
432
433        let timeout = if timeout == 0 { None } else { Some(Duration::from_secs(timeout.into())) };
434        Ok(self
435            .runtime
436            .block_on(self.inner.get_user_devices(&user_id, timeout))?
437            .devices()
438            .map(|d| d.into())
439            .collect())
440    }
441
442    /// Get the list of outgoing requests that need to be sent to the
443    /// homeserver.
444    ///
445    /// After the request was sent out and a successful response was received
446    /// the response body should be passed back to the state machine using the
447    /// [mark_request_as_sent()](Self::mark_request_as_sent) method.
448    ///
449    /// **Note**: This method call should be locked per call.
450    pub fn outgoing_requests(&self) -> Result<Vec<Request>, CryptoStoreError> {
451        Ok(self
452            .runtime
453            .block_on(self.inner.outgoing_requests())?
454            .into_iter()
455            .map(|r| r.into())
456            .collect())
457    }
458
459    /// Mark a request that was sent to the server as sent.
460    ///
461    /// # Arguments
462    ///
463    /// * `request_id` - The unique ID of the request that was sent out. This
464    ///   needs to be an UUID.
465    ///
466    /// * `request_type` - The type of the request that was sent out.
467    ///
468    /// * `response_body` - The body of the response that was received.
469    pub fn mark_request_as_sent(
470        &self,
471        request_id: String,
472        request_type: RequestType,
473        response_body: String,
474    ) -> Result<(), CryptoStoreError> {
475        let id: OwnedTransactionId = request_id.into();
476
477        let response = response_from_string(&response_body);
478
479        let response: OwnedResponse = match request_type {
480            RequestType::KeysUpload => {
481                KeysUploadResponse::try_from_http_response(response).map(Into::into)
482            }
483            RequestType::KeysQuery => {
484                KeysQueryResponse::try_from_http_response(response).map(Into::into)
485            }
486            RequestType::ToDevice => {
487                ToDeviceResponse::try_from_http_response(response).map(Into::into)
488            }
489            RequestType::KeysClaim => {
490                KeysClaimResponse::try_from_http_response(response).map(Into::into)
491            }
492            RequestType::SignatureUpload => {
493                SignatureUploadResponse::try_from_http_response(response).map(Into::into)
494            }
495            RequestType::KeysBackup => {
496                KeysBackupResponse::try_from_http_response(response).map(Into::into)
497            }
498            RequestType::RoomMessage => {
499                RoomMessageResponse::try_from_http_response(response).map(Into::into)
500            }
501        }
502        .expect("Can't convert json string to response");
503
504        self.runtime.block_on(self.inner.mark_request_as_sent(&id, &response))?;
505
506        Ok(())
507    }
508
509    /// Let the state machine know about E2EE related sync changes that we
510    /// received from the server.
511    ///
512    /// This needs to be called after every sync, ideally before processing
513    /// any other sync changes.
514    ///
515    /// # Arguments
516    ///
517    /// * `events` - A serialized array of to-device events we received in the
518    ///   current sync response.
519    ///
520    /// * `device_changes` - The list of devices that have changed in some way
521    ///   since the previous sync.
522    ///
523    /// * `key_counts` - The map of uploaded one-time key types and counts.
524    pub fn receive_sync_changes(
525        &self,
526        events: String,
527        device_changes: DeviceLists,
528        key_counts: HashMap<String, i32>,
529        unused_fallback_keys: Option<Vec<String>>,
530        next_batch_token: String,
531        decryption_settings: &DecryptionSettings,
532    ) -> Result<SyncChangesResult, CryptoStoreError> {
533        let to_device: ToDevice = serde_json::from_str(&events)?;
534        let device_changes: RumaDeviceLists = device_changes.into();
535        let key_counts: BTreeMap<OneTimeKeyAlgorithm, UInt> = key_counts
536            .into_iter()
537            .map(|(k, v)| {
538                (
539                    OneTimeKeyAlgorithm::from(k),
540                    v.clamp(0, i32::MAX)
541                        .try_into()
542                        .expect("Couldn't convert key counts into an UInt"),
543                )
544            })
545            .collect();
546
547        let unused_fallback_keys: Option<Vec<OneTimeKeyAlgorithm>> =
548            unused_fallback_keys.map(|u| u.into_iter().map(OneTimeKeyAlgorithm::from).collect());
549
550        let (to_device_events, room_key_infos) =
551            self.runtime.block_on(self.inner.receive_sync_changes(
552                matrix_sdk_crypto::EncryptionSyncChanges {
553                    to_device_events: to_device.events,
554                    changed_devices: &device_changes,
555                    one_time_keys_counts: &key_counts,
556                    unused_fallback_keys: unused_fallback_keys.as_deref(),
557                    next_batch_token: Some(next_batch_token),
558                },
559                decryption_settings,
560            ))?;
561
562        let to_device_events = to_device_events
563            .into_iter()
564            .map(|event| event.to_raw().json().get().to_owned())
565            .collect();
566        let room_key_infos = room_key_infos.into_iter().map(|info| info.into()).collect();
567
568        Ok(SyncChangesResult { to_device_events, room_key_infos })
569    }
570
571    /// Add the given list of users to be tracked, triggering a key query
572    /// request for them.
573    ///
574    /// The OlmMachine maintains a list of users whose devices we are keeping
575    /// track of: these are known as "tracked users". These must be users
576    /// that we share a room with, so that the server sends us updates for
577    /// their device lists.
578    ///
579    /// *Note*: Only users that aren't already tracked will be considered for an
580    /// update. It's safe to call this with already tracked users, it won't
581    /// result in excessive `/keys/query` requests.
582    ///
583    /// # Arguments
584    ///
585    /// `users` - The users that should be queued up for a key query.
586    pub fn update_tracked_users(&self, users: Vec<String>) -> Result<(), CryptoStoreError> {
587        let users: Vec<OwnedUserId> =
588            users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect();
589
590        self.runtime.block_on(self.inner.update_tracked_users(users.iter().map(Deref::deref)))?;
591
592        Ok(())
593    }
594
595    /// Check if the given user is considered to be tracked.
596    ///
597    /// A user can be marked for tracking using the
598    /// [`OlmMachine::update_tracked_users()`] method.
599    pub fn is_user_tracked(&self, user_id: String) -> Result<bool, CryptoStoreError> {
600        let user_id = parse_user_id(&user_id)?;
601        Ok(self.runtime.block_on(self.inner.tracked_users())?.contains(&user_id))
602    }
603
604    /// Generate one-time key claiming requests for all the users we are missing
605    /// sessions for.
606    ///
607    /// After the request was sent out and a successful response was received
608    /// the response body should be passed back to the state machine using the
609    /// [mark_request_as_sent()](Self::mark_request_as_sent) method.
610    ///
611    /// This method should be called every time before a call to
612    /// [`share_room_key()`](Self::share_room_key) is made.
613    ///
614    /// # Arguments
615    ///
616    /// * `users` - The list of users for which we would like to establish 1:1
617    ///   Olm sessions for.
618    pub fn get_missing_sessions(
619        &self,
620        users: Vec<String>,
621    ) -> Result<Option<Request>, CryptoStoreError> {
622        let users: Vec<OwnedUserId> =
623            users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect();
624
625        Ok(self
626            .runtime
627            .block_on(self.inner.get_missing_sessions(users.iter().map(Deref::deref)))?
628            .map(|r| r.into()))
629    }
630
631    /// Get the stored room settings, such as the encryption algorithm or
632    /// whether to encrypt only for trusted devices.
633    ///
634    /// These settings can be modified via
635    /// [set_room_algorithm()](Self::set_room_algorithm) and
636    /// [set_room_only_allow_trusted_devices()](Self::set_room_only_allow_trusted_devices)
637    /// methods.
638    pub fn get_room_settings(
639        &self,
640        room_id: String,
641    ) -> Result<Option<RoomSettings>, CryptoStoreError> {
642        let room_id = RoomId::parse(room_id)?;
643        let settings = self
644            .runtime
645            .block_on(self.inner.store().get_room_settings(&room_id))?
646            .map(|v| v.try_into())
647            .transpose()?;
648        Ok(settings)
649    }
650
651    /// Set the room algorithm used for encrypting messages to one of the
652    /// available variants
653    pub fn set_room_algorithm(
654        &self,
655        room_id: String,
656        algorithm: EventEncryptionAlgorithm,
657    ) -> Result<(), CryptoStoreError> {
658        let room_id = RoomId::parse(room_id)?;
659        self.runtime.block_on(async move {
660            let mut settings =
661                self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default();
662            settings.algorithm = algorithm.into();
663            self.inner
664                .store()
665                .save_changes(Changes {
666                    room_settings: HashMap::from([(room_id, settings)]),
667                    ..Default::default()
668                })
669                .await?;
670            Ok(())
671        })
672    }
673
674    /// Set flag whether this room should encrypt messages for untrusted
675    /// devices, or whether they should be excluded from the conversation.
676    ///
677    /// Note that per-room setting may be overridden by a global
678    /// [set_only_allow_trusted_devices()](Self::set_only_allow_trusted_devices)
679    /// method.
680    pub fn set_room_only_allow_trusted_devices(
681        &self,
682        room_id: String,
683        only_allow_trusted_devices: bool,
684    ) -> Result<(), CryptoStoreError> {
685        let room_id = RoomId::parse(room_id)?;
686        self.runtime.block_on(async move {
687            let mut settings =
688                self.inner.store().get_room_settings(&room_id).await?.unwrap_or_default();
689            settings.only_allow_trusted_devices = only_allow_trusted_devices;
690            self.inner
691                .store()
692                .save_changes(Changes {
693                    room_settings: HashMap::from([(room_id, settings)]),
694                    ..Default::default()
695                })
696                .await?;
697            Ok(())
698        })
699    }
700
701    /// Check whether there is a global flag to only encrypt messages for
702    /// trusted devices or for everyone.
703    ///
704    /// Note that if the global flag is false, individual rooms may still be
705    /// encrypting only for trusted devices, depending on the per-room
706    /// `only_allow_trusted_devices` flag.
707    pub fn get_only_allow_trusted_devices(&self) -> Result<bool, CryptoStoreError> {
708        let block = self.runtime.block_on(self.inner.store().get_only_allow_trusted_devices())?;
709        Ok(block)
710    }
711
712    /// Set global flag whether to encrypt messages for untrusted devices, or
713    /// whether they should be excluded from the conversation.
714    ///
715    /// Note that if enabled, it will override any per-room settings.
716    pub fn set_only_allow_trusted_devices(
717        &self,
718        only_allow_trusted_devices: bool,
719    ) -> Result<(), CryptoStoreError> {
720        self.runtime.block_on(
721            self.inner.store().set_only_allow_trusted_devices(only_allow_trusted_devices),
722        )?;
723        Ok(())
724    }
725
726    /// Share a room key with the given list of users for the given room.
727    ///
728    /// After the request was sent out and a successful response was received
729    /// the response body should be passed back to the state machine using the
730    /// [mark_request_as_sent()](Self::mark_request_as_sent) method.
731    ///
732    /// This method should be called every time before a call to
733    /// [`encrypt()`](Self::encrypt) with the given `room_id` is made.
734    ///
735    /// # Arguments
736    ///
737    /// * `room_id` - The unique id of the room, note that this doesn't strictly
738    ///   need to be a Matrix room, it just needs to be an unique identifier for
739    ///   the group that will participate in the conversation.
740    ///
741    /// * `users` - The list of users which are considered to be members of the
742    ///   room and should receive the room key.
743    ///
744    /// * `settings` - The settings that should be used for the room key.
745    pub fn share_room_key(
746        &self,
747        room_id: String,
748        users: Vec<String>,
749        settings: EncryptionSettings,
750    ) -> Result<Vec<Request>, CryptoStoreError> {
751        let users: Vec<OwnedUserId> =
752            users.into_iter().filter_map(|u| UserId::parse(u).ok()).collect();
753
754        let room_id = RoomId::parse(room_id)?;
755        let requests = self.runtime.block_on(self.inner.share_room_key(
756            &room_id,
757            users.iter().map(Deref::deref),
758            settings,
759        ))?;
760
761        Ok(requests.into_iter().map(|r| r.as_ref().into()).collect())
762    }
763
764    /// Encrypt the given event with the given type and content for the given
765    /// room.
766    ///
767    /// **Note**: A room key needs to be shared with the group of users that are
768    /// members in the given room. If this is not done this method will panic.
769    ///
770    /// The usual flow to encrypt an event using this state machine is as
771    /// follows:
772    ///
773    /// 1. Get the one-time key claim request to establish 1:1 Olm sessions for
774    ///    the room members of the room we wish to participate in. This is done
775    ///    using the [`get_missing_sessions()`](Self::get_missing_sessions)
776    ///    method. This method call should be locked per call.
777    ///
778    /// 2. Share a room key with all the room members using the
779    ///    [`share_room_key()`](Self::share_room_key). This method call should
780    ///    be locked per room.
781    ///
782    /// 3. Encrypt the event using this method.
783    ///
784    /// 4. Send the encrypted event to the server.
785    ///
786    /// After the room key is shared steps 1 and 2 will become noops, unless
787    /// there's some changes in the room membership or in the list of devices a
788    /// member has.
789    ///
790    /// # Arguments
791    ///
792    /// * `room_id` - The unique id of the room where the event will be sent to.
793    ///
794    /// * `even_type` - The type of the event.
795    ///
796    /// * `content` - The serialized content of the event.
797    pub fn encrypt(
798        &self,
799        room_id: String,
800        event_type: String,
801        content: String,
802    ) -> Result<String, CryptoStoreError> {
803        let room_id = RoomId::parse(room_id)?;
804        let content = serde_json::from_str(&content)?;
805
806        let result = self
807            .runtime
808            .block_on(self.inner.encrypt_room_event_raw(&room_id, &event_type, &content))
809            .expect("Encrypting an event produced an error");
810
811        Ok(serde_json::to_string(&result.content)?)
812    }
813
814    /// Encrypt the given event with the given type and content for the given
815    /// device. This method is used to send an event to a specific device.
816    ///
817    /// # Arguments
818    ///
819    /// * `user_id` - The ID of the user who owns the target device.
820    /// * `device_id` - The ID of the device to which the message will be sent.
821    /// * `event_type` - The event type.
822    /// * `content` - The serialized content of the event.
823    ///
824    /// # Returns
825    /// A `Result` containing the request to be sent out if the encryption was
826    /// successful. If the device is not found, the result will be `Ok(None)`.
827    ///
828    /// The caller should ensure that there is an olm session (see
829    /// `get_missing_sessions`) with the target device before calling this
830    /// method.
831    pub fn create_encrypted_to_device_request(
832        &self,
833        user_id: String,
834        device_id: String,
835        event_type: String,
836        content: String,
837        share_strategy: CollectStrategy,
838    ) -> Result<Option<Request>, CryptoStoreError> {
839        let user_id = parse_user_id(&user_id)?;
840        let device_id = device_id.as_str().into();
841        let content = serde_json::from_str(&content)?;
842
843        let device = self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))?;
844
845        if let Some(device) = device {
846            let encrypted_content = self.runtime.block_on(device.encrypt_event_raw(
847                &event_type,
848                &content,
849                share_strategy,
850            ))?;
851
852            let request = ToDeviceRequest::new(
853                user_id.as_ref(),
854                DeviceIdOrAllDevices::DeviceId(device_id.to_owned()),
855                "m.room.encrypted",
856                encrypted_content.cast(),
857            );
858
859            Ok(Some(request.into()))
860        } else {
861            Ok(None)
862        }
863    }
864
865    /// Decrypt the given event that was sent in the given room.
866    ///
867    /// # Arguments
868    ///
869    /// * `event` - The serialized encrypted version of the event.
870    ///
871    /// * `room_id` - The unique id of the room where the event was sent to.
872    ///
873    /// * `handle_verification_events` - if the supplied event is a verification
874    ///   event, use it to update the verification state. **Note**: it is
875    ///   recommended to avoid setting this flag to true and use the explicit
876    ///   [`OlmMachine::receive_verification_event`] method instead:
877    ///   verification events sometimes need preparation before we can handle
878    ///   them: see the documentation for
879    ///   [`OlmMachine::receive_verification_event`].
880    ///
881    /// * `strict_shields` - If `true`, messages will be decorated with strict
882    ///   warnings (use `false` to match legacy behaviour where unsafe keys have
883    ///   lower severity warnings and unverified identities are not decorated).
884    ///
885    /// * `decryption_settings` - The setting for decrypting messages.
886    pub fn decrypt_room_event(
887        &self,
888        event: String,
889        room_id: String,
890        handle_verification_events: bool,
891        strict_shields: bool,
892        decryption_settings: DecryptionSettings,
893    ) -> Result<DecryptedEvent, DecryptionError> {
894        // Element Android wants only the content and the type and will create a
895        // decrypted event with those two itself, this struct makes sure we
896        // throw away all the other fields.
897        #[derive(Deserialize, Serialize)]
898        struct Event<'a> {
899            #[serde(rename = "type")]
900            event_type: String,
901            #[serde(borrow)]
902            content: &'a RawValue,
903        }
904
905        let event: Raw<_> = serde_json::from_str(&event)?;
906        let room_id = RoomId::parse(room_id)?;
907
908        let decrypted = self.runtime.block_on(self.inner.decrypt_room_event(
909            &event,
910            &room_id,
911            &decryption_settings,
912        ))?;
913
914        if handle_verification_events
915            && let Ok(AnyTimelineEvent::MessageLike(e)) = decrypted.event.deserialize()
916        {
917            match &e {
918                AnyMessageLikeEvent::RoomMessage(MessageLikeEvent::Original(original_event)) => {
919                    if let MessageType::VerificationRequest(_) = &original_event.content.msgtype {
920                        self.runtime.block_on(self.inner.receive_verification_event(&e))?;
921                    }
922                }
923                _ if e.event_type().to_string().starts_with("m.key.verification") => {
924                    self.runtime.block_on(self.inner.receive_verification_event(&e))?;
925                }
926                _ => (),
927            }
928        }
929
930        let encryption_info = decrypted.encryption_info;
931
932        let event_json: Event<'_> = serde_json::from_str(decrypted.event.json().get())?;
933
934        Ok(match &encryption_info.algorithm_info {
935            AlgorithmInfo::MegolmV1AesSha2 {
936                curve25519_key,
937                sender_claimed_keys,
938                session_id: _,
939            } => DecryptedEvent {
940                clear_event: serde_json::to_string(&event_json)?,
941                sender_curve25519_key: curve25519_key.to_owned(),
942                claimed_ed25519_key: sender_claimed_keys.get(&DeviceKeyAlgorithm::Ed25519).cloned(),
943                forwarding_curve25519_chain: vec![],
944                shield_state: if strict_shields {
945                    encryption_info.verification_state.to_shield_state_strict().into()
946                } else {
947                    encryption_info.verification_state.to_shield_state_lax().into()
948                },
949            },
950            AlgorithmInfo::OlmV1Curve25519AesSha2 { .. } => {
951                // cannot happen because `decrypt_room_event` would have fail to decrypt olm for
952                // a room (EventError::UnsupportedAlgorithm)
953                panic!("Unsupported olm algorithm in room")
954            }
955        })
956    }
957
958    /// Request or re-request a room key that was used to encrypt the given
959    /// event.
960    ///
961    /// # Arguments
962    ///
963    /// * `event` - The undecryptable event that we would wish to request a room
964    ///   key for.
965    ///
966    /// * `room_id` - The id of the room the event was sent to.
967    pub fn request_room_key(
968        &self,
969        event: String,
970        room_id: String,
971    ) -> Result<KeyRequestPair, DecryptionError> {
972        let event: Raw<_> = serde_json::from_str(&event)?;
973        let room_id = RoomId::parse(room_id)?;
974
975        let (cancel, request) =
976            self.runtime.block_on(self.inner.request_room_key(&event, &room_id))?;
977
978        let cancellation = cancel.map(|r| r.into());
979        let key_request = request.into();
980
981        Ok(KeyRequestPair { cancellation, key_request })
982    }
983
984    /// Export all of our room keys.
985    ///
986    /// # Arguments
987    ///
988    /// * `passphrase` - The passphrase that should be used to encrypt the key
989    ///   export.
990    ///
991    /// * `rounds` - The number of rounds that should be used when expanding the
992    ///   passphrase into an key.
993    pub fn export_room_keys(
994        &self,
995        passphrase: String,
996        rounds: i32,
997    ) -> Result<String, CryptoStoreError> {
998        let keys = self.runtime.block_on(self.inner.store().export_room_keys(|_| true))?;
999
1000        let encrypted = encrypt_room_key_export(&keys, &passphrase, rounds as u32)
1001            .map_err(CryptoStoreError::Serialization)?;
1002
1003        Ok(encrypted)
1004    }
1005
1006    /// Import room keys from the given serialized key export.
1007    ///
1008    /// # Arguments
1009    ///
1010    /// * `keys` - The serialized version of the key export.
1011    ///
1012    /// * `passphrase` - The passphrase that was used to encrypt the key export.
1013    ///
1014    /// * `progress_listener` - A callback that can be used to introspect the
1015    ///   progress of the key import.
1016    pub fn import_room_keys(
1017        &self,
1018        keys: String,
1019        passphrase: String,
1020        progress_listener: Box<dyn ProgressListener>,
1021    ) -> Result<KeysImportResult, KeyImportError> {
1022        let keys = Cursor::new(keys);
1023        let keys = decrypt_room_key_export(keys, &passphrase)?;
1024        self.import_room_keys_helper(keys, None, progress_listener)
1025    }
1026
1027    /// Import room keys from the given serialized unencrypted key export.
1028    ///
1029    /// This method is the same as [`OlmMachine::import_room_keys`] but the
1030    /// decryption step is skipped and should be performed by the caller. This
1031    /// should be used if the room keys are coming from the server-side backup,
1032    /// the method will mark all imported room keys as backed up.
1033    ///
1034    /// **Note**: This has been deprecated. Use
1035    /// [`OlmMachine::import_room_keys_from_backup`] instead.
1036    ///
1037    /// # Arguments
1038    ///
1039    /// * `keys` - The serialized version of the unencrypted key export.
1040    ///
1041    /// * `progress_listener` - A callback that can be used to introspect the
1042    ///   progress of the key import.
1043    pub fn import_decrypted_room_keys(
1044        &self,
1045        keys: String,
1046        progress_listener: Box<dyn ProgressListener>,
1047    ) -> Result<KeysImportResult, KeyImportError> {
1048        // Assume that the keys came from the current backup version.
1049        let backup_version = self.runtime.block_on(self.inner.backup_machine().backup_version());
1050        let keys: Vec<Value> = serde_json::from_str(&keys)?;
1051        let keys = keys.into_iter().map(serde_json::from_value).filter_map(|k| k.ok()).collect();
1052        self.import_room_keys_helper(keys, backup_version.as_deref(), progress_listener)
1053    }
1054
1055    /// Import room keys from the given serialized unencrypted key export.
1056    ///
1057    /// This method is the same as [`OlmMachine::import_room_keys`] but the
1058    /// decryption step is skipped and should be performed by the caller. This
1059    /// should be used if the room keys are coming from the server-side backup.
1060    /// The method will mark all imported room keys as backed up.
1061    ///
1062    /// # Arguments
1063    ///
1064    /// * `keys` - The serialized version of the unencrypted key export.
1065    ///
1066    /// * `backup_version` - The version of the backup that these keys came
1067    ///   from.
1068    ///
1069    /// * `progress_listener` - A callback that can be used to introspect the
1070    ///   progress of the key import.
1071    pub fn import_room_keys_from_backup(
1072        &self,
1073        keys: String,
1074        backup_version: String,
1075        progress_listener: Box<dyn ProgressListener>,
1076    ) -> Result<KeysImportResult, KeyImportError> {
1077        let keys: Vec<Value> = serde_json::from_str(&keys)?;
1078        let keys = keys.into_iter().map(serde_json::from_value).filter_map(|k| k.ok()).collect();
1079        self.import_room_keys_helper(keys, Some(&backup_version), progress_listener)
1080    }
1081
1082    /// Discard the currently active room key for the given room if there is
1083    /// one.
1084    pub fn discard_room_key(&self, room_id: String) -> Result<(), CryptoStoreError> {
1085        let room_id = RoomId::parse(room_id)?;
1086
1087        self.runtime.block_on(self.inner.discard_room_key(&room_id))?;
1088
1089        Ok(())
1090    }
1091
1092    /// Receive an unencrypted verification event.
1093    ///
1094    /// This method can be used to pass verification events that are happening
1095    /// in unencrypted rooms to the `OlmMachine`.
1096    ///
1097    /// **Note**: This has been deprecated.
1098    pub fn receive_unencrypted_verification_event(
1099        &self,
1100        event: String,
1101        room_id: String,
1102    ) -> Result<(), CryptoStoreError> {
1103        self.receive_verification_event(event, room_id)
1104    }
1105
1106    /// Receive a verification event.
1107    ///
1108    /// This method can be used to pass verification events that are happening
1109    /// in rooms to the `OlmMachine`. The event should be in the decrypted form.
1110    ///
1111    /// **Note**: If the supplied event is an `m.room.message` event with
1112    /// `msgtype: m.key.verification.request`, then the device information for
1113    /// the sending user must be up-to-date before calling this method
1114    /// (otherwise, the request will be ignored). It is hard to guarantee this
1115    /// is the case, but you can maximize your chances by explicitly making a
1116    /// request to /keys/query for the user's device info, and processing the
1117    /// response with [`OlmMachine::mark_request_as_sent`].
1118    pub fn receive_verification_event(
1119        &self,
1120        event: String,
1121        room_id: String,
1122    ) -> Result<(), CryptoStoreError> {
1123        let room_id = RoomId::parse(room_id)?;
1124        let event: AnySyncMessageLikeEvent = serde_json::from_str(&event)?;
1125
1126        let event = event.into_full_event(room_id);
1127
1128        self.runtime.block_on(self.inner.receive_verification_event(&event))?;
1129
1130        Ok(())
1131    }
1132
1133    /// Get all the verification requests that we share with the given user.
1134    ///
1135    /// # Arguments
1136    ///
1137    /// * `user_id` - The ID of the user for which we would like to fetch the
1138    ///   verification requests.
1139    pub fn get_verification_requests(&self, user_id: String) -> Vec<Arc<VerificationRequest>> {
1140        let Ok(user_id) = UserId::parse(user_id) else {
1141            return vec![];
1142        };
1143
1144        self.inner
1145            .get_verification_requests(&user_id)
1146            .into_iter()
1147            .map(|v| {
1148                VerificationRequest { inner: v, runtime: self.runtime.handle().to_owned() }.into()
1149            })
1150            .collect()
1151    }
1152
1153    /// Get a verification requests that we share with the given user with the
1154    /// given flow id.
1155    ///
1156    /// # Arguments
1157    ///
1158    /// * `user_id` - The ID of the user for which we would like to fetch the
1159    ///   verification requests.
1160    ///
1161    /// * `flow_id` - The ID that uniquely identifies the verification flow.
1162    pub fn get_verification_request(
1163        &self,
1164        user_id: String,
1165        flow_id: String,
1166    ) -> Option<Arc<VerificationRequest>> {
1167        let user_id = UserId::parse(user_id).ok()?;
1168
1169        self.inner.get_verification_request(&user_id, flow_id).map(|v| {
1170            VerificationRequest { inner: v, runtime: self.runtime.handle().to_owned() }.into()
1171        })
1172    }
1173
1174    /// Get an m.key.verification.request content for the given user.
1175    ///
1176    /// # Arguments
1177    ///
1178    /// * `user_id` - The ID of the user which we would like to request to
1179    ///   verify.
1180    ///
1181    /// * `methods` - The list of verification methods we want to advertise to
1182    ///   support.
1183    pub fn verification_request_content(
1184        &self,
1185        user_id: String,
1186        methods: Vec<String>,
1187    ) -> Result<Option<String>, CryptoStoreError> {
1188        let user_id = parse_user_id(&user_id)?;
1189
1190        let identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?;
1191
1192        let methods = methods.into_iter().map(VerificationMethod::from).collect();
1193
1194        Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
1195            let content = identity.verification_request_content(Some(methods));
1196            Some(serde_json::to_string(&content)?)
1197        } else {
1198            None
1199        })
1200    }
1201
1202    /// Request a verification flow to begin with the given user in the given
1203    /// room.
1204    ///
1205    /// # Arguments
1206    ///
1207    /// * `user_id` - The ID of the user which we would like to request to
1208    ///   verify.
1209    ///
1210    /// * `room_id` - The ID of the room that represents a DM with the given
1211    ///   user.
1212    ///
1213    /// * `event_id` - The event ID of the `m.key.verification.request` event
1214    ///   that we sent out to request the verification to begin. The content for
1215    ///   this request can be created using the [verification_request_content()]
1216    ///   method.
1217    ///
1218    /// * `methods` - The list of verification methods we advertised as
1219    ///   supported in the `m.key.verification.request` event.
1220    ///
1221    /// [verification_request_content()]: Self::verification_request_content
1222    pub fn request_verification(
1223        &self,
1224        user_id: String,
1225        room_id: String,
1226        event_id: String,
1227        methods: Vec<String>,
1228    ) -> Result<Option<Arc<VerificationRequest>>, CryptoStoreError> {
1229        let user_id = parse_user_id(&user_id)?;
1230        let event_id = EventId::parse(event_id)?;
1231        let room_id = RoomId::parse(room_id)?;
1232
1233        let identity = self.runtime.block_on(self.inner.get_identity(&user_id, None))?;
1234
1235        let methods = methods.into_iter().map(VerificationMethod::from).collect();
1236
1237        Ok(if let Some(identity) = identity.and_then(|i| i.other()) {
1238            let request = identity.request_verification(&room_id, &event_id, Some(methods));
1239
1240            Some(
1241                VerificationRequest { inner: request, runtime: self.runtime.handle().to_owned() }
1242                    .into(),
1243            )
1244        } else {
1245            None
1246        })
1247    }
1248
1249    /// Request a verification flow to begin with the given user's device.
1250    ///
1251    /// # Arguments
1252    ///
1253    /// * `user_id` - The ID of the user which we would like to request to
1254    ///   verify.
1255    ///
1256    /// * `device_id` - The ID of the device that we wish to verify.
1257    ///
1258    /// * `methods` - The list of verification methods we advertised as
1259    ///   supported in the `m.key.verification.request` event.
1260    pub fn request_verification_with_device(
1261        &self,
1262        user_id: String,
1263        device_id: String,
1264        methods: Vec<String>,
1265    ) -> Result<Option<RequestVerificationResult>, CryptoStoreError> {
1266        let user_id = parse_user_id(&user_id)?;
1267        let device_id = device_id.as_str().into();
1268
1269        let methods = methods.into_iter().map(VerificationMethod::from).collect();
1270
1271        Ok(
1272            if let Some(device) =
1273                self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))?
1274            {
1275                let (verification, request) = device.request_verification_with_methods(methods);
1276
1277                Some(RequestVerificationResult {
1278                    verification: VerificationRequest {
1279                        inner: verification,
1280                        runtime: self.runtime.handle().to_owned(),
1281                    }
1282                    .into(),
1283                    request: request.into(),
1284                })
1285            } else {
1286                None
1287            },
1288        )
1289    }
1290
1291    /// Request a verification flow to begin with our other devices.
1292    ///
1293    /// # Arguments
1294    ///
1295    /// `methods` - The list of verification methods we want to advertise to
1296    /// support.
1297    pub fn request_self_verification(
1298        &self,
1299        methods: Vec<String>,
1300    ) -> Result<Option<RequestVerificationResult>, CryptoStoreError> {
1301        let identity =
1302            self.runtime.block_on(self.inner.get_identity(self.inner.user_id(), None))?;
1303
1304        let methods = methods.into_iter().map(VerificationMethod::from).collect();
1305
1306        Ok(if let Some(identity) = identity.and_then(|i| i.own()) {
1307            let (verification, request) =
1308                self.runtime.block_on(identity.request_verification_with_methods(methods))?;
1309            Some(RequestVerificationResult {
1310                verification: VerificationRequest {
1311                    inner: verification,
1312                    runtime: self.runtime.handle().to_owned(),
1313                }
1314                .into(),
1315                request: request.into(),
1316            })
1317        } else {
1318            None
1319        })
1320    }
1321
1322    /// Get a verification flow object for the given user with the given flow
1323    /// id.
1324    ///
1325    /// # Arguments
1326    ///
1327    /// * `user_id` - The ID of the user for which we would like to fetch the
1328    ///   verification.
1329    ///
1330    /// * `flow_id` - The ID that uniquely identifies the verification flow.
1331    pub fn get_verification(&self, user_id: String, flow_id: String) -> Option<Arc<Verification>> {
1332        let user_id = UserId::parse(user_id).ok()?;
1333
1334        self.inner
1335            .get_verification(&user_id, &flow_id)
1336            .map(|v| Verification { inner: v, runtime: self.runtime.handle().to_owned() }.into())
1337    }
1338
1339    /// Start short auth string verification with a device without going
1340    /// through a verification request first.
1341    ///
1342    /// **Note**: This has been largely deprecated and the
1343    /// [request_verification_with_device()] method should be used instead.
1344    ///
1345    /// # Arguments
1346    ///
1347    /// * `user_id` - The ID of the user for which we would like to start the
1348    ///   SAS verification.
1349    ///
1350    /// * `device_id` - The ID of device we would like to verify.
1351    ///
1352    /// [request_verification_with_device()]: Self::request_verification_with_device
1353    pub fn start_sas_with_device(
1354        &self,
1355        user_id: String,
1356        device_id: String,
1357    ) -> Result<Option<StartSasResult>, CryptoStoreError> {
1358        let user_id = parse_user_id(&user_id)?;
1359        let device_id = device_id.as_str().into();
1360
1361        Ok(
1362            if let Some(device) =
1363                self.runtime.block_on(self.inner.get_device(&user_id, device_id, None))?
1364            {
1365                let (sas, request) = self.runtime.block_on(device.start_verification())?;
1366
1367                Some(StartSasResult {
1368                    sas: Sas { inner: Box::new(sas), runtime: self.runtime.handle().to_owned() }
1369                        .into(),
1370                    request: request.into(),
1371                })
1372            } else {
1373                None
1374            },
1375        )
1376    }
1377
1378    /// Create a new private cross signing identity and create a request to
1379    /// upload the public part of it to the server.
1380    pub fn bootstrap_cross_signing(
1381        &self,
1382    ) -> Result<BootstrapCrossSigningResult, BootstrapCrossSigningError> {
1383        Ok(self.runtime.block_on(self.inner.bootstrap_cross_signing(true))?.into())
1384    }
1385
1386    /// Export all our private cross signing keys.
1387    ///
1388    /// The export will contain the seed for the ed25519 keys as a base64
1389    /// encoded string.
1390    ///
1391    /// This method returns `None` if we don't have any private cross signing
1392    /// keys.
1393    pub fn export_cross_signing_keys(
1394        &self,
1395    ) -> Result<Option<CrossSigningKeyExport>, CryptoStoreError> {
1396        Ok(self.runtime.block_on(self.inner.export_cross_signing_keys())?.map(|e| e.into()))
1397    }
1398
1399    /// Import our private cross signing keys.
1400    ///
1401    /// The export needs to contain the seed for the ed25519 keys as a base64
1402    /// encoded string.
1403    pub fn import_cross_signing_keys(
1404        &self,
1405        export: CrossSigningKeyExport,
1406    ) -> Result<(), SecretImportError> {
1407        self.runtime.block_on(self.inner.import_cross_signing_keys(export.into()))?;
1408
1409        Ok(())
1410    }
1411
1412    /// Export all the secrets we have in the store into a serialized
1413    /// SecretsBundle.
1414    ///
1415    /// This method will export all the private cross-signing keys and, if
1416    /// available, the private part of a backup key and its accompanying
1417    /// version.
1418    ///
1419    /// The method will fail if we don't have all three private cross-signing
1420    /// keys available.
1421    ///
1422    /// **Warning**: Only export this and share it with a trusted recipient,
1423    /// i.e. if an existing device is sharing this with a new device.
1424    pub fn export_secrets_bundle(&self) -> Result<String, SecretsBundleExportError> {
1425        let bundle = self.runtime.block_on(self.inner.store().export_secrets_bundle())?;
1426
1427        Ok(serde_json::to_string(&bundle)?)
1428    }
1429
1430    /// Request missing local secrets from our devices (cross signing private
1431    /// keys, megolm backup). This will ask the sdk to create outgoing
1432    /// request to get the missing secrets.
1433    ///
1434    /// The requests will be processed as soon as `outgoing_requests()` is
1435    /// called to process them.
1436    pub fn query_missing_secrets_from_other_sessions(&self) -> Result<bool, CryptoStoreError> {
1437        Ok(self.runtime.block_on(self.inner.query_missing_secrets_from_other_sessions())?)
1438    }
1439
1440    /// Activate the given backup key to be used with the given backup version.
1441    ///
1442    /// **Warning**: The caller needs to make sure that the given `BackupKey` is
1443    /// trusted, otherwise we might be encrypting room keys that a malicious
1444    /// party could decrypt.
1445    ///
1446    /// The [`OlmMachine::verify_backup`] method can be used to so.
1447    pub fn enable_backup_v1(
1448        &self,
1449        key: MegolmV1BackupKey,
1450        version: String,
1451    ) -> Result<(), DecodeError> {
1452        let backup_key = RustBackupKey::from_base64(&key.public_key)?;
1453        backup_key.set_version(version);
1454
1455        self.runtime.block_on(self.inner.backup_machine().enable_backup_v1(backup_key))?;
1456
1457        Ok(())
1458    }
1459
1460    /// Are we able to encrypt room keys.
1461    ///
1462    /// This returns true if we have an active `BackupKey` and backup version
1463    /// registered with the state machine.
1464    pub fn backup_enabled(&self) -> bool {
1465        self.runtime.block_on(self.inner.backup_machine().enabled())
1466    }
1467
1468    /// Disable and reset our backup state.
1469    ///
1470    /// This will remove any pending backup request, remove the backup key and
1471    /// reset the backup state of each room key we have.
1472    pub fn disable_backup(&self) -> Result<(), CryptoStoreError> {
1473        Ok(self.runtime.block_on(self.inner.backup_machine().disable_backup())?)
1474    }
1475
1476    /// Encrypt a batch of room keys and return a request that needs to be sent
1477    /// out to backup the room keys.
1478    pub fn backup_room_keys(&self) -> Result<Option<Request>, CryptoStoreError> {
1479        let request = self.runtime.block_on(self.inner.backup_machine().backup())?;
1480
1481        let request = request.map(|r| r.into());
1482
1483        Ok(request)
1484    }
1485
1486    /// Get the number of backed up room keys and the total number of room keys.
1487    pub fn room_key_counts(&self) -> Result<RoomKeyCounts, CryptoStoreError> {
1488        Ok(self.runtime.block_on(self.inner.backup_machine().room_key_counts())?.into())
1489    }
1490
1491    /// Store the recovery key in the crypto store.
1492    ///
1493    /// This is useful if the client wants to support gossiping of the backup
1494    /// key.
1495    pub fn save_recovery_key(
1496        &self,
1497        key: Option<Arc<BackupRecoveryKey>>,
1498        version: Option<String>,
1499    ) -> Result<(), CryptoStoreError> {
1500        let key = key.map(|k| {
1501            // We need to clone here due to FFI limitations but RecoveryKey does
1502            // not want to expose clone since it's private key material.
1503            let mut encoded = k.to_base64();
1504            let key = BackupDecryptionKey::from_base64(&encoded)
1505                .expect("Encoding and decoding from base64 should always work");
1506            encoded.zeroize();
1507            key
1508        });
1509        Ok(self.runtime.block_on(self.inner.backup_machine().save_decryption_key(key, version))?)
1510    }
1511
1512    /// Get the backup keys we have saved in our crypto store.
1513    pub fn get_backup_keys(&self) -> Result<Option<Arc<BackupKeys>>, CryptoStoreError> {
1514        Ok(self
1515            .runtime
1516            .block_on(self.inner.backup_machine().get_backup_keys())?
1517            .try_into()
1518            .ok()
1519            .map(Arc::new))
1520    }
1521
1522    /// Sign the given message using our device key and if available cross
1523    /// signing master key.
1524    pub fn sign(
1525        &self,
1526        message: String,
1527    ) -> Result<HashMap<String, HashMap<String, String>>, CryptoStoreError> {
1528        Ok(self
1529            .runtime
1530            .block_on(self.inner.sign(&message))?
1531            .into_iter()
1532            .map(|(k, v)| {
1533                (
1534                    k.to_string(),
1535                    v.into_iter()
1536                        .map(|(k, v)| {
1537                            (
1538                                k.to_string(),
1539                                match v {
1540                                    Ok(s) => s.to_base64(),
1541                                    Err(i) => i.source,
1542                                },
1543                            )
1544                        })
1545                        .collect(),
1546                )
1547            })
1548            .collect())
1549    }
1550
1551    /// Check if the given backup has been verified by us or by another of our
1552    /// devices that we trust.
1553    ///
1554    /// The `backup_info` should be a JSON encoded object with the following
1555    /// format:
1556    ///
1557    /// ```json
1558    /// {
1559    ///     "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
1560    ///     "auth_data": {
1561    ///         "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
1562    ///         "signatures": {}
1563    ///     }
1564    /// }
1565    /// ```
1566    pub fn verify_backup(
1567        &self,
1568        backup_info: String,
1569    ) -> Result<SignatureVerification, CryptoStoreError> {
1570        let backup_info = serde_json::from_str(&backup_info)?;
1571
1572        Ok(self
1573            .runtime
1574            .block_on(self.inner.backup_machine().verify_backup(backup_info, false))?
1575            .into())
1576    }
1577
1578    /// Manage dehydrated devices.
1579    pub fn dehydrated_devices(&self) -> Arc<DehydratedDevices> {
1580        DehydratedDevices {
1581            inner: ManuallyDrop::new(self.inner.dehydrated_devices()),
1582            runtime: self.runtime.handle().to_owned(),
1583        }
1584        .into()
1585    }
1586}
1587
1588impl OlmMachine {
1589    fn import_room_keys_helper(
1590        &self,
1591        keys: Vec<ExportedRoomKey>,
1592        from_backup_version: Option<&str>,
1593        progress_listener: Box<dyn ProgressListener>,
1594    ) -> Result<KeysImportResult, KeyImportError> {
1595        let listener = |progress: usize, total: usize| {
1596            progress_listener.on_progress(progress as i32, total as i32)
1597        };
1598
1599        let result = self.runtime.block_on(self.inner.store().import_room_keys(
1600            keys,
1601            from_backup_version,
1602            listener,
1603        ))?;
1604
1605        Ok(KeysImportResult {
1606            imported: result.imported_count as i64,
1607            total: result.total_count as i64,
1608            keys: result
1609                .keys
1610                .into_iter()
1611                .map(|(r, m)| {
1612                    (
1613                        r.to_string(),
1614                        m.into_iter().map(|(s, k)| (s, k.into_iter().collect())).collect(),
1615                    )
1616                })
1617                .collect(),
1618        })
1619    }
1620}