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