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