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