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