1use std::{
27 collections::{BTreeMap, BTreeSet},
28 sync::Arc,
29};
30
31use ruma::{
32 api::client::backup::RoomKeyBackup, serde::Raw, DeviceId, DeviceKeyAlgorithm, OwnedDeviceId,
33 OwnedRoomId, OwnedTransactionId, RoomId, TransactionId,
34};
35use tokio::sync::RwLock;
36use tracing::{debug, info, instrument, trace, warn};
37
38use crate::{
39 olm::{BackedUpRoomKey, ExportedRoomKey, InboundGroupSession, SignedJsonObject},
40 store::{
41 types::{BackupDecryptionKey, BackupKeys, Changes, RoomKeyCounts},
42 Store,
43 },
44 types::{requests::KeysBackupRequest, MegolmV1AuthData, RoomKeyBackupInfo, Signatures},
45 CryptoStoreError, Device, RoomKeyImportResult, SignatureError,
46};
47
48mod keys;
49
50pub use keys::{DecodeError, DecryptionError, MegolmV1BackupKey};
51
52#[derive(Debug, Clone)]
59pub struct BackupMachine {
60 store: Store,
61 backup_key: Arc<RwLock<Option<MegolmV1BackupKey>>>,
62 pending_backup: Arc<RwLock<Option<PendingBackup>>>,
63}
64
65type SenderKey = String;
66type SessionId = String;
67
68#[derive(Debug, Clone)]
69struct PendingBackup {
70 request_id: OwnedTransactionId,
71 request: KeysBackupRequest,
72 sessions: BTreeMap<OwnedRoomId, BTreeMap<SenderKey, BTreeSet<SessionId>>>,
73}
74
75#[derive(Clone, Debug, Default, PartialEq, Eq)]
77pub struct SignatureVerification {
78 pub device_signature: SignatureState,
81 pub user_identity_signature: SignatureState,
84 pub other_signatures: BTreeMap<OwnedDeviceId, SignatureState>,
87}
88
89impl SignatureVerification {
90 pub fn trusted(&self) -> bool {
99 self.device_signature.trusted()
100 || self.user_identity_signature.trusted()
101 || self.other_signatures.values().any(|s| s.trusted())
102 }
103}
104
105#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
107#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
108pub enum SignatureState {
109 #[default]
111 Missing,
112 Invalid,
114 ValidButNotTrusted,
117 ValidAndTrusted,
120}
121
122impl SignatureState {
123 pub fn trusted(self) -> bool {
125 self == SignatureState::ValidAndTrusted
126 }
127
128 pub fn signed(self) -> bool {
130 self == SignatureState::ValidButNotTrusted && self == SignatureState::ValidAndTrusted
131 }
132}
133
134impl BackupMachine {
135 const BACKUP_BATCH_SIZE: usize = 100;
136
137 pub(crate) fn new(store: Store, backup_key: Option<MegolmV1BackupKey>) -> Self {
138 Self {
139 store,
140 backup_key: RwLock::new(backup_key).into(),
141 pending_backup: RwLock::new(None).into(),
142 }
143 }
144
145 pub async fn enabled(&self) -> bool {
147 self.backup_key.read().await.as_ref().is_some_and(|b| b.backup_version().is_some())
148 }
149
150 fn check_own_device_signature(
152 &self,
153 signatures: &Signatures,
154 auth_data: &str,
155 ) -> SignatureState {
156 match self.store.static_account().has_signed_raw(signatures, auth_data) {
157 Ok(_) => SignatureState::ValidAndTrusted,
158 Err(e) => match e {
159 SignatureError::NoSignatureFound => SignatureState::Missing,
160 _ => SignatureState::Invalid,
161 },
162 }
163 }
164
165 async fn check_own_identity_signature(
168 &self,
169 signatures: &Signatures,
170 auth_data: &str,
171 ) -> Result<SignatureState, CryptoStoreError> {
172 let user_id = &self.store.static_account().user_id;
173 let identity = self.store.get_identity(user_id).await?;
174
175 let ret = if let Some(identity) = identity.and_then(|i| i.own()) {
176 match identity.master_key().has_signed_raw(signatures, auth_data) {
177 Ok(_) => {
178 if identity.is_verified() {
179 SignatureState::ValidAndTrusted
180 } else {
181 SignatureState::ValidButNotTrusted
182 }
183 }
184 Err(e) => match e {
185 SignatureError::NoSignatureFound => SignatureState::Missing,
186 _ => SignatureState::Invalid,
187 },
188 }
189 } else {
190 SignatureState::Missing
191 };
192
193 Ok(ret)
194 }
195
196 fn backup_signed_by_device(
199 &self,
200 device: Device,
201 signatures: &Signatures,
202 auth_data: &str,
203 ) -> SignatureState {
204 if device.has_signed_raw(signatures, auth_data).is_ok() {
205 if device.is_verified() {
206 SignatureState::ValidAndTrusted
207 } else {
208 SignatureState::ValidButNotTrusted
209 }
210 } else {
211 SignatureState::Invalid
212 }
213 }
214
215 async fn test_device_signatures(
218 &self,
219 signatures: &Signatures,
220 auth_data: &str,
221 compute_all_signatures: bool,
222 ) -> Result<BTreeMap<OwnedDeviceId, SignatureState>, CryptoStoreError> {
223 let mut result = BTreeMap::new();
224
225 if let Some(user_signatures) = signatures.get(&self.store.static_account().user_id) {
226 for device_key_id in user_signatures.keys() {
227 if device_key_id.algorithm() == DeviceKeyAlgorithm::Ed25519 {
228 if device_key_id.key_name() == self.store.static_account().device_id {
231 continue;
232 }
233
234 let state = self
235 .test_ed25519_device_signature(
236 device_key_id.key_name(),
237 signatures,
238 auth_data,
239 )
240 .await?;
241
242 result.insert(device_key_id.key_name().to_owned(), state);
243
244 if state.trusted() && !compute_all_signatures {
247 break;
248 }
249 }
250 }
251 }
252
253 Ok(result)
254 }
255
256 async fn test_ed25519_device_signature(
257 &self,
258 device_id: &DeviceId,
259 signatures: &Signatures,
260 auth_data: &str,
261 ) -> Result<SignatureState, CryptoStoreError> {
262 let device = self.store.get_device(self.store.user_id(), device_id).await?;
266 trace!(?device_id, "Checking backup auth data for device");
267
268 if let Some(device) = device {
269 Ok(self.backup_signed_by_device(device, signatures, auth_data))
270 } else {
271 trace!(?device_id, "Device not found, can't check signature");
272 Ok(SignatureState::Missing)
273 }
274 }
275
276 async fn verify_auth_data_v1(
277 &self,
278 auth_data: MegolmV1AuthData,
279 compute_all_signatures: bool,
280 ) -> Result<SignatureVerification, CryptoStoreError> {
281 let serialized_auth_data = match auth_data.to_canonical_json() {
282 Ok(s) => s,
283 Err(e) => {
284 warn!(error =? e, "Error while verifying backup, can't canonicalize auth data");
285 return Ok(Default::default());
286 }
287 };
288
289 let device_signature =
291 self.check_own_device_signature(&auth_data.signatures, &serialized_auth_data);
292 let user_identity_signature =
294 self.check_own_identity_signature(&auth_data.signatures, &serialized_auth_data).await?;
295
296 let other_signatures = if !(device_signature.trusted() || user_identity_signature.trusted())
299 || compute_all_signatures
300 {
301 self.test_device_signatures(
302 &auth_data.signatures,
303 &serialized_auth_data,
304 compute_all_signatures,
305 )
306 .await?
307 } else {
308 Default::default()
309 };
310
311 Ok(SignatureVerification { device_signature, user_identity_signature, other_signatures })
312 }
313
314 pub async fn verify_backup(
329 &self,
330 backup_info: RoomKeyBackupInfo,
331 compute_all_signatures: bool,
332 ) -> Result<SignatureVerification, CryptoStoreError> {
333 trace!(?backup_info, "Verifying backup auth data");
334
335 if let RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(data) = backup_info {
336 self.verify_auth_data_v1(data, compute_all_signatures).await
337 } else {
338 Ok(Default::default())
339 }
340 }
341
342 pub async fn sign_backup(
351 &self,
352 backup_info: &mut RoomKeyBackupInfo,
353 ) -> Result<(), SignatureError> {
354 if let RoomKeyBackupInfo::MegolmBackupV1Curve25519AesSha2(data) = backup_info {
355 let canonical_json = data.to_canonical_json()?;
356
357 let private_identity = self.store.private_identity();
358 let identity = private_identity.lock().await;
359
360 if let Some(key_id) = identity.master_key_id().await {
361 if let Ok(signature) = identity.sign(&canonical_json).await {
362 data.signatures.add_signature(
363 self.store.user_id().to_owned(),
364 key_id,
365 signature,
366 );
367 }
368 }
369
370 let cache = self.store.cache().await?;
371 let account = cache.account().await?;
372 let key_id = account.signing_key_id();
373 let signature = account.sign(&canonical_json);
374 data.signatures.add_signature(self.store.user_id().to_owned(), key_id, signature);
375
376 Ok(())
377 } else {
378 Err(SignatureError::UnsupportedAlgorithm)
379 }
380 }
381
382 pub async fn enable_backup_v1(&self, key: MegolmV1BackupKey) -> Result<(), CryptoStoreError> {
391 if key.backup_version().is_some() {
392 *self.backup_key.write().await = Some(key.clone());
393 info!(backup_key = ?key, "Activated a backup");
394 } else {
395 warn!(backup_key = ?key, "Tried to activate a backup without having the backup key uploaded");
396 }
397
398 Ok(())
399 }
400
401 pub async fn room_key_counts(&self) -> Result<RoomKeyCounts, CryptoStoreError> {
403 let backup_version = self.backup_key.read().await.as_ref().and_then(|k| k.backup_version());
404 self.store.inbound_group_session_counts(backup_version.as_deref()).await
405 }
406
407 #[instrument(skip(self))]
412 pub async fn disable_backup(&self) -> Result<(), CryptoStoreError> {
413 debug!("Disabling key backup and resetting backup state for room keys");
414
415 self.backup_key.write().await.take();
416 self.pending_backup.write().await.take();
417
418 self.store.reset_backup_state().await?;
419
420 debug!("Done disabling backup");
421
422 Ok(())
423 }
424
425 pub async fn backup_version(&self) -> Option<String> {
429 self.backup_key.read().await.as_ref().and_then(|k| k.backup_version())
430 }
431
432 pub async fn save_decryption_key(
437 &self,
438 backup_decryption_key: Option<BackupDecryptionKey>,
439 version: Option<String>,
440 ) -> Result<(), CryptoStoreError> {
441 let changes =
442 Changes { backup_decryption_key, backup_version: version, ..Default::default() };
443 self.store.save_changes(changes).await
444 }
445
446 pub async fn get_backup_keys(&self) -> Result<BackupKeys, CryptoStoreError> {
448 self.store.load_backup_keys().await
449 }
450
451 pub async fn backup(
454 &self,
455 ) -> Result<Option<(OwnedTransactionId, KeysBackupRequest)>, CryptoStoreError> {
456 let mut request = self.pending_backup.write().await;
457
458 if let Some(request) = &*request {
459 trace!("Backing up, returning an existing request");
460
461 Ok(Some((request.request_id.clone(), request.request.clone())))
462 } else {
463 trace!("Backing up, creating a new request");
464
465 let new_request = self.backup_helper().await?;
466 *request = new_request.clone();
467
468 Ok(new_request.map(|r| (r.request_id, r.request)))
469 }
470 }
471
472 pub(crate) async fn mark_request_as_sent(
473 &self,
474 request_id: &TransactionId,
475 ) -> Result<(), CryptoStoreError> {
476 let mut request = self.pending_backup.write().await;
477 if let Some(r) = &*request {
478 if r.request_id == request_id {
479 let room_and_session_ids: Vec<(&RoomId, &str)> = r
480 .sessions
481 .iter()
482 .flat_map(|(room_id, sender_key_to_session_ids)| {
483 std::iter::repeat(room_id).zip(sender_key_to_session_ids.values().flatten())
484 })
485 .map(|(room_id, session_id)| (room_id.as_ref(), session_id.as_str()))
486 .collect();
487
488 trace!(request_id = ?r.request_id, keys = ?r.sessions, "Marking room keys as backed up");
489
490 self.store
491 .mark_inbound_group_sessions_as_backed_up(
492 &r.request.version,
493 &room_and_session_ids,
494 )
495 .await?;
496
497 trace!(
498 request_id = ?r.request_id,
499 keys = ?r.sessions,
500 "Marked room keys as backed up"
501 );
502
503 *request = None;
504 } else {
505 warn!(
506 expected = ?r.request_id,
507 got = ?request_id,
508 "Tried to mark a pending backup as sent but the request id didn't match"
509 );
510 }
511 } else {
512 warn!(
513 ?request_id,
514 "Tried to mark a pending backup as sent but there isn't a backup pending"
515 );
516 }
517
518 Ok(())
519 }
520
521 async fn backup_helper(&self) -> Result<Option<PendingBackup>, CryptoStoreError> {
522 let Some(backup_key) = &*self.backup_key.read().await else {
523 warn!("Trying to backup room keys but no backup key was found");
524 return Ok(None);
525 };
526
527 let Some(version) = backup_key.backup_version() else {
528 warn!("Trying to backup room keys but the backup key wasn't uploaded");
529 return Ok(None);
530 };
531
532 let sessions =
533 self.store.inbound_group_sessions_for_backup(&version, Self::BACKUP_BATCH_SIZE).await?;
534
535 if sessions.is_empty() {
536 trace!(?backup_key, "No room keys need to be backed up");
537 return Ok(None);
538 }
539
540 let key_count = sessions.len();
541 let (backup, session_record) = Self::backup_keys(sessions, backup_key).await;
542
543 info!(
544 key_count = key_count,
545 keys = ?session_record,
546 ?backup_key,
547 "Successfully created a room keys backup request"
548 );
549
550 let request = PendingBackup {
551 request_id: TransactionId::new(),
552 request: KeysBackupRequest { version, rooms: backup },
553 sessions: session_record,
554 };
555
556 Ok(Some(request))
557 }
558
559 async fn backup_keys(
561 sessions: Vec<InboundGroupSession>,
562 backup_key: &MegolmV1BackupKey,
563 ) -> (
564 BTreeMap<OwnedRoomId, RoomKeyBackup>,
565 BTreeMap<OwnedRoomId, BTreeMap<SenderKey, BTreeSet<SessionId>>>,
566 ) {
567 let mut backup: BTreeMap<OwnedRoomId, RoomKeyBackup> = BTreeMap::new();
568 let mut session_record: BTreeMap<OwnedRoomId, BTreeMap<SenderKey, BTreeSet<SessionId>>> =
569 BTreeMap::new();
570
571 for session in sessions {
572 let room_id = session.room_id().to_owned();
573 let session_id = session.session_id().to_owned();
574 let sender_key = session.sender_key().to_owned();
575 let session = backup_key.encrypt(session).await;
576
577 session_record
578 .entry(room_id.to_owned())
579 .or_default()
580 .entry(sender_key.to_base64())
581 .or_default()
582 .insert(session_id.clone());
583
584 let session = Raw::new(&session).expect("Can't serialize a backed up room key");
585
586 backup
587 .entry(room_id)
588 .or_insert_with(|| RoomKeyBackup::new(BTreeMap::new()))
589 .sessions
590 .insert(session_id, session);
591 }
592
593 (backup, session_record)
594 }
595
596 #[deprecated(note = "Use the OlmMachine::store::import_room_keys method instead")]
607 pub async fn import_backed_up_room_keys(
608 &self,
609 room_keys: BTreeMap<OwnedRoomId, BTreeMap<String, BackedUpRoomKey>>,
610 progress_listener: impl Fn(usize, usize),
611 ) -> Result<RoomKeyImportResult, CryptoStoreError> {
612 let mut decrypted_room_keys = vec![];
613
614 for (room_id, room_keys) in room_keys {
615 for (session_id, room_key) in room_keys {
616 let room_key = ExportedRoomKey::from_backed_up_room_key(
617 room_id.to_owned(),
618 session_id,
619 room_key,
620 );
621
622 decrypted_room_keys.push(room_key);
623 }
624 }
625
626 let backup_version = self.backup_version().await;
631
632 self.store
633 .import_room_keys(decrypted_room_keys, backup_version.as_deref(), progress_listener)
634 .await
635 }
636}
637
638#[cfg(test)]
639mod tests {
640 use std::collections::BTreeMap;
641
642 use assert_matches2::assert_let;
643 use matrix_sdk_test::async_test;
644 use ruma::{device_id, room_id, user_id, CanonicalJsonValue, DeviceId, RoomId, UserId};
645 use serde_json::json;
646
647 use super::BackupMachine;
648 use crate::{
649 olm::BackedUpRoomKey,
650 store::{
651 types::{BackupDecryptionKey, Changes},
652 CryptoStore, MemoryStore,
653 },
654 types::RoomKeyBackupInfo,
655 OlmError, OlmMachine,
656 };
657
658 fn room_key() -> BackedUpRoomKey {
659 let json = json!({
660 "algorithm": "m.megolm.v1.aes-sha2",
661 "sender_key": "DeHIg4gwhClxzFYcmNntPNF9YtsdZbmMy8+3kzCMXHA",
662 "session_key": "AQAAAABvWMNZjKFtebYIePKieQguozuoLgzeY6wKcyJjLJcJtQgy1dPqTBD12U+XrYLrRHn\
663 lKmxoozlhFqJl456+9hlHCL+yq+6ScFuBHtJepnY1l2bdLb4T0JMDkNsNErkiLiLnD6yp3J\
664 DSjIhkdHxmup/huygrmroq6/L5TaThEoqvW4DPIuO14btKudsS34FF82pwjKS4p6Mlch+0e\
665 fHAblQV",
666 "sender_claimed_keys":{},
667 "forwarding_curve25519_key_chain":[]
668 });
669
670 serde_json::from_value(json)
671 .expect("We should be able to deserialize our backed up room key")
672 }
673
674 fn alice_id() -> &'static UserId {
675 user_id!("@alice:example.org")
676 }
677
678 fn alice_device_id() -> &'static DeviceId {
679 device_id!("JLAFKJWSCS")
680 }
681
682 fn room_id() -> &'static RoomId {
683 room_id!("!test:localhost")
684 }
685
686 fn room_id2() -> &'static RoomId {
687 room_id!("!test2:localhost")
688 }
689
690 async fn backup_flow(machine: OlmMachine) -> Result<(), OlmError> {
691 let backup_machine = machine.backup_machine();
692 let backup_version = current_backup_version(backup_machine).await;
693
694 let counts =
695 backup_machine.store.inbound_group_session_counts(backup_version.as_deref()).await?;
696
697 assert_eq!(counts.total, 0, "Initially no keys exist");
698 assert_eq!(counts.backed_up, 0, "Initially no backed up keys exist");
699
700 machine.create_outbound_group_session_with_defaults_test_helper(room_id()).await?;
701 machine.create_outbound_group_session_with_defaults_test_helper(room_id2()).await?;
702
703 let counts =
704 backup_machine.store.inbound_group_session_counts(backup_version.as_deref()).await?;
705 assert_eq!(counts.total, 2, "Two room keys need to exist in the store");
706 assert_eq!(counts.backed_up, 0, "No room keys have been backed up yet");
707
708 let decryption_key = BackupDecryptionKey::new().expect("Can't create new recovery key");
709 let backup_key = decryption_key.megolm_v1_public_key();
710 backup_key.set_version("1".to_owned());
711
712 backup_machine.enable_backup_v1(backup_key).await?;
713
714 let (request_id, _) =
715 backup_machine.backup().await?.expect("Created a backup request successfully");
716 assert_eq!(
717 Some(&request_id),
718 backup_machine.backup().await?.as_ref().map(|(request_id, _)| request_id),
719 "Calling backup again without uploading creates the same backup request"
720 );
721
722 backup_machine.mark_request_as_sent(&request_id).await?;
723 let backup_version = current_backup_version(backup_machine).await;
724
725 let counts =
726 backup_machine.store.inbound_group_session_counts(backup_version.as_deref()).await?;
727 assert_eq!(counts.total, 2);
728 assert_eq!(counts.backed_up, 2, "All room keys have been backed up");
729
730 assert!(
731 backup_machine.backup().await?.is_none(),
732 "No room keys need to be backed up, no request needs to be created"
733 );
734
735 backup_machine.disable_backup().await?;
736 let backup_version = current_backup_version(backup_machine).await;
737
738 let counts =
739 backup_machine.store.inbound_group_session_counts(backup_version.as_deref()).await?;
740 assert_eq!(counts.total, 2);
741 assert_eq!(
742 counts.backed_up, 0,
743 "Disabling the backup resets the backup flag on the room keys"
744 );
745
746 Ok(())
747 }
748
749 async fn current_backup_version(backup_machine: &BackupMachine) -> Option<String> {
750 backup_machine.backup_key.read().await.as_ref().and_then(|k| k.backup_version())
751 }
752
753 #[async_test]
754 async fn test_memory_store_backups() -> Result<(), OlmError> {
755 let machine = OlmMachine::new(alice_id(), alice_device_id()).await;
756
757 backup_flow(machine).await
758 }
759
760 #[async_test]
761 async fn test_verify_auth_data() -> Result<(), OlmError> {
762 let machine = OlmMachine::new(alice_id(), alice_device_id()).await;
763 let backup_machine = machine.backup_machine();
764
765 let auth_data = json!({
766 "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
767 });
768
769 let backup_version = json!({
770 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
771 "auth_data": auth_data,
772 });
773
774 let canonical_json: CanonicalJsonValue =
775 auth_data.clone().try_into().expect("Canonicalizing should always work");
776 let serialized = canonical_json.to_string();
777
778 let backup_version: RoomKeyBackupInfo = serde_json::from_value(backup_version).unwrap();
779
780 let state = backup_machine
781 .verify_backup(backup_version, false)
782 .await
783 .expect("Verifying should work");
784 assert!(!state.trusted());
785 assert!(!state.device_signature.trusted());
786 assert!(!state.user_identity_signature.trusted());
787 assert!(!state.other_signatures.values().any(|s| s.trusted()));
788
789 let signatures = machine.sign(&serialized).await?;
790
791 let backup_version = json!({
792 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
793 "auth_data": {
794 "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
795 "signatures": signatures,
796 }
797 });
798 let backup_version: RoomKeyBackupInfo = serde_json::from_value(backup_version).unwrap();
799
800 let state = backup_machine
801 .verify_backup(backup_version, false)
802 .await
803 .expect("Verifying should work");
804
805 assert!(state.trusted());
806 assert!(state.device_signature.trusted());
807 assert!(!state.user_identity_signature.trusted());
808 assert!(!state.other_signatures.values().any(|s| s.trusted()));
809
810 machine
811 .bootstrap_cross_signing(true)
812 .await
813 .expect("Bootstrapping a new identity always works");
814
815 let signatures = machine.sign(&serialized).await?;
816
817 let backup_version = json!({
818 "algorithm": "m.megolm_backup.v1.curve25519-aes-sha2",
819 "auth_data": {
820 "public_key":"XjhWTCjW7l59pbfx9tlCBQolfnIQWARoKOzjTOPSlWM",
821 "signatures": signatures,
822 }
823 });
824 let backup_version: RoomKeyBackupInfo = serde_json::from_value(backup_version).unwrap();
825
826 let state = backup_machine
827 .verify_backup(backup_version, false)
828 .await
829 .expect("Verifying should work");
830
831 assert!(state.trusted());
832 assert!(state.device_signature.trusted());
833 assert!(state.user_identity_signature.trusted());
834 assert!(!state.other_signatures.values().any(|s| s.trusted()));
835
836 Ok(())
837 }
838
839 #[async_test]
840 async fn test_import_backed_up_room_keys() {
841 let machine = OlmMachine::new(alice_id(), alice_device_id()).await;
842 let backup_machine = machine.backup_machine();
843
844 let decryption_key = BackupDecryptionKey::new().expect("Couldn't create new recovery key");
846 let backup_key = decryption_key.megolm_v1_public_key();
847 backup_key.set_version("1".to_owned());
848 backup_machine.enable_backup_v1(backup_key).await.expect("Couldn't enable backup");
849
850 let room_id = room_id!("!DovneieKSTkdHKpIXy:morpheus.localhost");
851 let session_id = "gM8i47Xhu0q52xLfgUXzanCMpLinoyVyH7R58cBuVBU";
852 let room_key = room_key();
853
854 let room_keys: BTreeMap<_, BTreeMap<_, _>> = BTreeMap::from([(
855 room_id.to_owned(),
856 BTreeMap::from([(session_id.to_owned(), room_key)]),
857 )]);
858
859 let session = machine.store().get_inbound_group_session(room_id, session_id).await.unwrap();
860
861 assert!(session.is_none(), "Initially we should not have the session in the store");
862
863 #[allow(deprecated)]
864 backup_machine
865 .import_backed_up_room_keys(room_keys, |_, _| {})
866 .await
867 .expect("We should be able to import a room key");
868
869 let session = machine.store().get_inbound_group_session(room_id, session_id).await.unwrap();
872 assert_let!(Some(session) = session);
873 assert!(
874 session.backed_up(),
875 "If a session was imported from a backup, it should be considered to be backed up"
876 );
877 assert!(session.has_been_imported());
878
879 let backup_request =
881 backup_machine.backup().await.expect("We should be able to create a backup request");
882 assert!(
883 backup_request.is_none(),
884 "If a session was imported from backup, it should not be backed up again."
885 );
886 }
887
888 #[async_test]
889 async fn test_sign_backup_info() {
890 let machine = OlmMachine::new(alice_id(), alice_device_id()).await;
891 let backup_machine = machine.backup_machine();
892
893 let decryption_key = BackupDecryptionKey::new().unwrap();
894 let mut backup_info = decryption_key.to_backup_info();
895
896 let result = backup_machine.verify_backup(backup_info.to_owned(), false).await.unwrap();
897
898 assert!(!result.trusted());
899
900 backup_machine.sign_backup(&mut backup_info).await.unwrap();
901
902 let result = backup_machine.verify_backup(backup_info, false).await.unwrap();
903
904 assert!(result.trusted());
905 }
906
907 #[async_test]
908 async fn test_fix_backup_key_mismatch() {
909 let store = MemoryStore::new();
910
911 let backup_decryption_key = BackupDecryptionKey::new().unwrap();
912
913 store
914 .save_changes(Changes {
915 backup_decryption_key: Some(backup_decryption_key.clone()),
916 backup_version: Some("1".to_owned()),
917 ..Default::default()
918 })
919 .await
920 .unwrap();
921
922 let alice =
925 OlmMachine::with_store(alice_id(), alice_device_id(), store, None).await.unwrap();
926
927 let binding = alice.backup_machine().backup_key.read().await;
928 let machine_backup_key = binding.as_ref().unwrap();
929
930 assert_eq!(
931 machine_backup_key.to_base64(),
932 backup_decryption_key.megolm_v1_public_key().to_base64(),
933 "The OlmMachine loaded the wrong backup key."
934 );
935 }
936}