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