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