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