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