1use std::{
16 cmp::Ordering,
17 fmt,
18 ops::Deref,
19 sync::{
20 atomic::{AtomicBool, Ordering::SeqCst},
21 Arc,
22 },
23};
24
25use ruma::{
26 events::room::history_visibility::HistoryVisibility, serde::JsonObject, DeviceKeyAlgorithm,
27 OwnedRoomId, RoomId,
28};
29use serde::{Deserialize, Serialize};
30use tokio::sync::Mutex;
31use vodozemac::{
32 megolm::{
33 DecryptedMessage, DecryptionError, InboundGroupSession as InnerSession,
34 InboundGroupSessionPickle, MegolmMessage, SessionConfig, SessionOrdering,
35 },
36 Curve25519PublicKey, Ed25519PublicKey, PickleError,
37};
38
39use super::{
40 BackedUpRoomKey, ExportedRoomKey, OutboundGroupSession, SenderData, SenderDataType,
41 SessionCreationError, SessionKey,
42};
43use crate::{
44 error::{EventError, MegolmResult},
45 types::{
46 deserialize_curve_key,
47 events::{
48 forwarded_room_key::{
49 ForwardedMegolmV1AesSha2Content, ForwardedMegolmV2AesSha2Content,
50 ForwardedRoomKeyContent,
51 },
52 olm_v1::DecryptedForwardedRoomKeyEvent,
53 room::encrypted::{EncryptedEvent, RoomEventEncryptionScheme},
54 room_key,
55 },
56 serialize_curve_key, EventEncryptionAlgorithm, SigningKeys,
57 },
58};
59
60#[derive(Clone)]
66pub(crate) struct SessionCreatorInfo {
67 pub curve25519_key: Curve25519PublicKey,
82
83 pub signing_keys: Arc<SigningKeys<DeviceKeyAlgorithm>>,
97}
98
99#[derive(Clone)]
111pub struct InboundGroupSession {
112 inner: Arc<Mutex<InnerSession>>,
113
114 session_id: Arc<str>,
117
118 first_known_index: u32,
121
122 pub(crate) creator_info: SessionCreatorInfo,
126
127 pub sender_data: SenderData,
133
134 pub room_id: OwnedRoomId,
136
137 imported: bool,
144
145 algorithm: Arc<EventEncryptionAlgorithm>,
150
151 history_visibility: Arc<Option<HistoryVisibility>>,
154
155 backed_up: Arc<AtomicBool>,
157}
158
159impl InboundGroupSession {
160 pub fn new(
180 sender_key: Curve25519PublicKey,
181 signing_key: Ed25519PublicKey,
182 room_id: &RoomId,
183 session_key: &SessionKey,
184 sender_data: SenderData,
185 encryption_algorithm: EventEncryptionAlgorithm,
186 history_visibility: Option<HistoryVisibility>,
187 ) -> Result<Self, SessionCreationError> {
188 let config = OutboundGroupSession::session_config(&encryption_algorithm)?;
189
190 let session = InnerSession::new(session_key, config);
191 let session_id = session.session_id();
192 let first_known_index = session.first_known_index();
193
194 let mut keys = SigningKeys::new();
195 keys.insert(DeviceKeyAlgorithm::Ed25519, signing_key.into());
196
197 Ok(InboundGroupSession {
198 inner: Arc::new(Mutex::new(session)),
199 history_visibility: history_visibility.into(),
200 session_id: session_id.into(),
201 first_known_index,
202 creator_info: SessionCreatorInfo {
203 curve25519_key: sender_key,
204 signing_keys: keys.into(),
205 },
206 sender_data,
207 room_id: room_id.into(),
208 imported: false,
209 algorithm: encryption_algorithm.into(),
210 backed_up: AtomicBool::new(false).into(),
211 })
212 }
213
214 pub fn from_room_key_content(
227 sender_key: Curve25519PublicKey,
228 signing_key: Ed25519PublicKey,
229 content: &room_key::MegolmV1AesSha2Content,
230 ) -> Result<Self, SessionCreationError> {
231 let room_key::MegolmV1AesSha2Content { room_id, session_id: _, session_key, .. } = content;
232
233 Self::new(
234 sender_key,
235 signing_key,
236 room_id,
237 session_key,
238 SenderData::unknown(),
239 EventEncryptionAlgorithm::MegolmV1AesSha2,
240 None,
241 )
242 }
243
244 pub fn from_export(exported_session: &ExportedRoomKey) -> Result<Self, SessionCreationError> {
250 Self::try_from(exported_session)
251 }
252
253 pub async fn pickle(&self) -> PickledInboundGroupSession {
256 let pickle = self.inner.lock().await.pickle();
257
258 PickledInboundGroupSession {
259 pickle,
260 sender_key: self.creator_info.curve25519_key,
261 signing_key: (*self.creator_info.signing_keys).clone(),
262 sender_data: self.sender_data.clone(),
263 room_id: self.room_id().to_owned(),
264 imported: self.imported,
265 backed_up: self.backed_up(),
266 history_visibility: self.history_visibility.as_ref().clone(),
267 algorithm: (*self.algorithm).to_owned(),
268 }
269 }
270
271 pub async fn export(&self) -> ExportedRoomKey {
276 self.export_at_index(self.first_known_index()).await
277 }
278
279 pub fn sender_key(&self) -> Curve25519PublicKey {
281 self.creator_info.curve25519_key
282 }
283
284 pub fn backed_up(&self) -> bool {
286 self.backed_up.load(SeqCst)
287 }
288
289 pub fn reset_backup_state(&self) {
291 self.backed_up.store(false, SeqCst)
292 }
293
294 pub fn mark_as_backed_up(&self) {
297 self.backed_up.store(true, SeqCst)
298 }
299
300 pub fn signing_keys(&self) -> &SigningKeys<DeviceKeyAlgorithm> {
302 &self.creator_info.signing_keys
303 }
304
305 pub async fn export_at_index(&self, message_index: u32) -> ExportedRoomKey {
307 let message_index = std::cmp::max(self.first_known_index(), message_index);
308
309 let session_key =
310 self.inner.lock().await.export_at(message_index).expect("Can't export session");
311
312 ExportedRoomKey {
313 algorithm: self.algorithm().to_owned(),
314 room_id: self.room_id().to_owned(),
315 sender_key: self.creator_info.curve25519_key,
316 session_id: self.session_id().to_owned(),
317 forwarding_curve25519_key_chain: vec![],
318 sender_claimed_keys: (*self.creator_info.signing_keys).clone(),
319 session_key,
320 }
321 }
322
323 pub fn from_pickle(pickle: PickledInboundGroupSession) -> Result<Self, PickleError> {
335 let session: InnerSession = pickle.pickle.into();
336 let first_known_index = session.first_known_index();
337 let session_id = session.session_id();
338
339 Ok(InboundGroupSession {
340 inner: Mutex::new(session).into(),
341 session_id: session_id.into(),
342 creator_info: SessionCreatorInfo {
343 curve25519_key: pickle.sender_key,
344 signing_keys: pickle.signing_key.into(),
345 },
346 sender_data: pickle.sender_data,
347 history_visibility: pickle.history_visibility.into(),
348 first_known_index,
349 room_id: (*pickle.room_id).into(),
350 backed_up: AtomicBool::from(pickle.backed_up).into(),
351 algorithm: pickle.algorithm.into(),
352 imported: pickle.imported,
353 })
354 }
355
356 pub fn room_id(&self) -> &RoomId {
358 &self.room_id
359 }
360
361 pub fn session_id(&self) -> &str {
363 &self.session_id
364 }
365
366 pub fn algorithm(&self) -> &EventEncryptionAlgorithm {
369 &self.algorithm
370 }
371
372 pub fn first_known_index(&self) -> u32 {
374 self.first_known_index
375 }
376
377 pub fn has_been_imported(&self) -> bool {
380 self.imported
381 }
382
383 pub async fn compare(&self, other: &InboundGroupSession) -> SessionOrdering {
386 if Arc::ptr_eq(&self.inner, &other.inner) {
389 SessionOrdering::Equal
390 } else if self.sender_key() != other.sender_key()
391 || self.signing_keys() != other.signing_keys()
392 || self.algorithm() != other.algorithm()
393 || self.room_id() != other.room_id()
394 {
395 SessionOrdering::Unconnected
396 } else {
397 let mut other_inner = other.inner.lock().await;
398
399 match self.inner.lock().await.compare(&mut other_inner) {
400 SessionOrdering::Equal => {
401 match self.sender_data.compare_trust_level(&other.sender_data) {
402 Ordering::Less => SessionOrdering::Worse,
403 Ordering::Equal => SessionOrdering::Equal,
404 Ordering::Greater => SessionOrdering::Better,
405 }
406 }
407 result => result,
408 }
409 }
410 }
411
412 pub(crate) async fn decrypt_helper(
421 &self,
422 message: &MegolmMessage,
423 ) -> Result<DecryptedMessage, DecryptionError> {
424 self.inner.lock().await.decrypt(message)
425 }
426
427 pub async fn to_backup(&self) -> BackedUpRoomKey {
430 self.export().await.into()
431 }
432
433 pub async fn decrypt(&self, event: &EncryptedEvent) -> MegolmResult<(JsonObject, u32)> {
439 let decrypted = match &event.content.scheme {
440 RoomEventEncryptionScheme::MegolmV1AesSha2(c) => {
441 self.decrypt_helper(&c.ciphertext).await?
442 }
443 #[cfg(feature = "experimental-algorithms")]
444 RoomEventEncryptionScheme::MegolmV2AesSha2(c) => {
445 self.decrypt_helper(&c.ciphertext).await?
446 }
447 RoomEventEncryptionScheme::Unknown(_) => {
448 return Err(EventError::UnsupportedAlgorithm.into());
449 }
450 };
451
452 let plaintext = String::from_utf8_lossy(&decrypted.plaintext);
453
454 let mut decrypted_object = serde_json::from_str::<JsonObject>(&plaintext)?;
455
456 let server_ts: i64 = event.origin_server_ts.0.into();
457
458 decrypted_object.insert("sender".to_owned(), event.sender.to_string().into());
459 decrypted_object.insert("event_id".to_owned(), event.event_id.to_string().into());
460 decrypted_object.insert("origin_server_ts".to_owned(), server_ts.into());
461
462 let room_id = decrypted_object
463 .get("room_id")
464 .and_then(|r| r.as_str().and_then(|r| RoomId::parse(r).ok()));
465
466 if room_id.as_deref() != Some(self.room_id()) {
469 return Err(EventError::MismatchedRoom(self.room_id().to_owned(), room_id).into());
470 }
471
472 decrypted_object.insert(
473 "unsigned".to_owned(),
474 serde_json::to_value(&event.unsigned).unwrap_or_default(),
475 );
476
477 if let Some(decrypted_content) =
478 decrypted_object.get_mut("content").and_then(|c| c.as_object_mut())
479 {
480 if !decrypted_content.contains_key("m.relates_to") {
481 if let Some(relation) = &event.content.relates_to {
482 decrypted_content.insert("m.relates_to".to_owned(), relation.to_owned());
483 }
484 }
485 }
486
487 Ok((decrypted_object, decrypted.message_index))
488 }
489
490 #[cfg(test)]
492 pub(crate) fn mark_as_imported(&mut self) {
493 self.imported = true;
494 }
495
496 pub fn sender_data_type(&self) -> SenderDataType {
500 self.sender_data.to_type()
501 }
502}
503
504#[cfg(not(tarpaulin_include))]
505impl fmt::Debug for InboundGroupSession {
506 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507 f.debug_struct("InboundGroupSession").field("session_id", &self.session_id()).finish()
508 }
509}
510
511impl PartialEq for InboundGroupSession {
512 fn eq(&self, other: &Self) -> bool {
513 self.session_id() == other.session_id()
514 }
515}
516
517#[derive(Serialize, Deserialize)]
522#[allow(missing_debug_implementations)]
523pub struct PickledInboundGroupSession {
524 pub pickle: InboundGroupSessionPickle,
526 #[serde(deserialize_with = "deserialize_curve_key", serialize_with = "serialize_curve_key")]
528 pub sender_key: Curve25519PublicKey,
529 pub signing_key: SigningKeys<DeviceKeyAlgorithm>,
531 #[serde(default)]
533 pub sender_data: SenderData,
534 pub room_id: OwnedRoomId,
536 pub imported: bool,
539 #[serde(default)]
541 pub backed_up: bool,
542 pub history_visibility: Option<HistoryVisibility>,
544 #[serde(default = "default_algorithm")]
546 pub algorithm: EventEncryptionAlgorithm,
547}
548
549fn default_algorithm() -> EventEncryptionAlgorithm {
550 EventEncryptionAlgorithm::MegolmV1AesSha2
551}
552
553impl TryFrom<&ExportedRoomKey> for InboundGroupSession {
554 type Error = SessionCreationError;
555
556 fn try_from(key: &ExportedRoomKey) -> Result<Self, Self::Error> {
557 let config = OutboundGroupSession::session_config(&key.algorithm)?;
558 let session = InnerSession::import(&key.session_key, config);
559 let first_known_index = session.first_known_index();
560
561 Ok(InboundGroupSession {
562 inner: Mutex::new(session).into(),
563 session_id: key.session_id.to_owned().into(),
564 creator_info: SessionCreatorInfo {
565 curve25519_key: key.sender_key,
566 signing_keys: key.sender_claimed_keys.to_owned().into(),
567 },
568 sender_data: SenderData::default(),
571 history_visibility: None.into(),
572 first_known_index,
573 room_id: key.room_id.to_owned(),
574 imported: true,
575 algorithm: key.algorithm.to_owned().into(),
576 backed_up: AtomicBool::from(false).into(),
577 })
578 }
579}
580
581impl From<&ForwardedMegolmV1AesSha2Content> for InboundGroupSession {
582 fn from(value: &ForwardedMegolmV1AesSha2Content) -> Self {
583 let session = InnerSession::import(&value.session_key, SessionConfig::version_1());
584 let session_id = session.session_id().into();
585 let first_known_index = session.first_known_index();
586
587 InboundGroupSession {
588 inner: Mutex::new(session).into(),
589 session_id,
590 creator_info: SessionCreatorInfo {
591 curve25519_key: value.claimed_sender_key,
592 signing_keys: SigningKeys::from([(
593 DeviceKeyAlgorithm::Ed25519,
594 value.claimed_ed25519_key.into(),
595 )])
596 .into(),
597 },
598 sender_data: SenderData::default(),
601 history_visibility: None.into(),
602 first_known_index,
603 room_id: value.room_id.to_owned(),
604 imported: true,
605 algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
606 backed_up: AtomicBool::from(false).into(),
607 }
608 }
609}
610
611impl From<&ForwardedMegolmV2AesSha2Content> for InboundGroupSession {
612 fn from(value: &ForwardedMegolmV2AesSha2Content) -> Self {
613 let session = InnerSession::import(&value.session_key, SessionConfig::version_2());
614 let session_id = session.session_id().into();
615 let first_known_index = session.first_known_index();
616
617 InboundGroupSession {
618 inner: Mutex::new(session).into(),
619 session_id,
620 creator_info: SessionCreatorInfo {
621 curve25519_key: value.claimed_sender_key,
622 signing_keys: value.claimed_signing_keys.to_owned().into(),
623 },
624 sender_data: SenderData::default(),
627 history_visibility: None.into(),
628 first_known_index,
629 room_id: value.room_id.to_owned(),
630 imported: true,
631 algorithm: EventEncryptionAlgorithm::MegolmV1AesSha2.into(),
632 backed_up: AtomicBool::from(false).into(),
633 }
634 }
635}
636
637impl TryFrom<&DecryptedForwardedRoomKeyEvent> for InboundGroupSession {
638 type Error = SessionCreationError;
639
640 fn try_from(value: &DecryptedForwardedRoomKeyEvent) -> Result<Self, Self::Error> {
641 match &value.content {
642 ForwardedRoomKeyContent::MegolmV1AesSha2(c) => Ok(Self::from(c.deref())),
643 #[cfg(feature = "experimental-algorithms")]
644 ForwardedRoomKeyContent::MegolmV2AesSha2(c) => Ok(Self::from(c.deref())),
645 ForwardedRoomKeyContent::Unknown(c) => {
646 Err(SessionCreationError::Algorithm(c.algorithm.to_owned()))
647 }
648 }
649 }
650}
651
652#[cfg(test)]
653mod tests {
654 use assert_matches2::assert_let;
655 use matrix_sdk_test::async_test;
656 use ruma::{
657 device_id, events::room::history_visibility::HistoryVisibility, room_id, user_id, DeviceId,
658 UserId,
659 };
660 use vodozemac::{
661 megolm::{SessionKey, SessionOrdering},
662 Curve25519PublicKey, Ed25519PublicKey,
663 };
664
665 use crate::{
666 olm::{InboundGroupSession, KnownSenderData, SenderData},
667 types::EventEncryptionAlgorithm,
668 Account,
669 };
670
671 fn alice_id() -> &'static UserId {
672 user_id!("@alice:example.org")
673 }
674
675 fn alice_device_id() -> &'static DeviceId {
676 device_id!("ALICEDEVICE")
677 }
678
679 #[async_test]
680 async fn test_can_deserialise_pickled_session_without_sender_data() {
681 let pickle = r#"
683 {
684 "pickle": {
685 "initial_ratchet": {
686 "inner": [ 124, 251, 213, 204, 108, 247, 54, 7, 179, 162, 15, 107, 154, 215,
687 220, 46, 123, 113, 120, 162, 225, 246, 237, 203, 125, 102, 190, 212,
688 229, 195, 136, 185, 26, 31, 77, 140, 144, 181, 152, 177, 46, 105,
689 202, 6, 53, 158, 157, 170, 31, 155, 130, 87, 214, 110, 143, 55, 68,
690 138, 41, 35, 242, 230, 194, 15, 16, 145, 116, 94, 89, 35, 79, 145,
691 245, 117, 204, 173, 166, 178, 49, 131, 143, 61, 61, 15, 211, 167, 17,
692 2, 79, 110, 149, 200, 223, 23, 185, 200, 29, 64, 55, 39, 147, 167,
693 205, 224, 159, 101, 218, 249, 203, 30, 175, 174, 48, 252, 40, 131,
694 52, 135, 91, 57, 211, 96, 105, 58, 55, 68, 250, 24 ],
695 "counter": 0
696 },
697 "signing_key": [ 93, 185, 171, 61, 173, 100, 51, 9, 157, 180, 214, 39, 131, 80, 118,
698 130, 199, 232, 163, 197, 45, 23, 227, 100, 151, 59, 19, 102, 38,
699 149, 43, 38 ],
700 "signing_key_verified": true,
701 "config": {
702 "version": "V1"
703 }
704 },
705 "sender_key": "AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8",
706 "signing_key": {
707 "ed25519": "wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww"
708 },
709 "room_id": "!test:localhost",
710 "forwarding_chains": ["tb6kQKjk+SJl2KnfQ0lKVOZl6gDFMcsb9HcUP9k/4hc"],
711 "imported": false,
712 "backed_up": false,
713 "history_visibility": "shared",
714 "algorithm": "m.megolm.v1.aes-sha2"
715 }
716 "#;
717
718 let deserialized = serde_json::from_str(pickle).unwrap();
720
721 let unpickled = InboundGroupSession::from_pickle(deserialized).unwrap();
723
724 assert_eq!(unpickled.session_id(), "XbmrPa1kMwmdtNYng1B2gsfoo8UtF+NklzsTZiaVKyY");
726
727 assert_let!(
730 SenderData::UnknownDevice { legacy_session, owner_check_failed } =
731 unpickled.sender_data
732 );
733 assert!(legacy_session);
734 assert!(!owner_check_failed);
735 }
736
737 #[async_test]
738 async fn test_can_serialise_pickled_session_with_sender_data() {
739 let igs = InboundGroupSession::new(
741 Curve25519PublicKey::from_base64("AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8")
742 .unwrap(),
743 Ed25519PublicKey::from_base64("wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww").unwrap(),
744 room_id!("!test:localhost"),
745 &create_session_key(),
746 SenderData::unknown(),
747 EventEncryptionAlgorithm::MegolmV1AesSha2,
748 Some(HistoryVisibility::Shared),
749 )
750 .unwrap();
751
752 let pickled = igs.pickle().await;
754
755 let serialised = serde_json::to_string(&pickled).unwrap();
757
758 let expected_inner = vec![
762 193, 203, 223, 152, 33, 132, 200, 168, 24, 197, 79, 174, 231, 202, 45, 245, 128, 131,
763 178, 165, 148, 37, 241, 214, 178, 218, 25, 33, 68, 48, 153, 104, 122, 6, 249, 198, 97,
764 226, 214, 75, 64, 128, 25, 138, 98, 90, 138, 93, 52, 206, 174, 3, 84, 149, 101, 140,
765 238, 156, 103, 107, 124, 144, 139, 104, 253, 5, 100, 251, 186, 118, 208, 87, 31, 218,
766 123, 234, 103, 34, 246, 100, 39, 90, 216, 72, 187, 86, 202, 150, 100, 116, 204, 254,
767 10, 154, 216, 133, 61, 250, 75, 100, 195, 63, 138, 22, 17, 13, 156, 123, 195, 132, 111,
768 95, 250, 24, 236, 0, 246, 93, 230, 100, 211, 165, 211, 190, 181, 87, 42, 181,
769 ];
770 assert_eq!(
771 serde_json::from_str::<serde_json::Value>(&serialised).unwrap(),
772 serde_json::json!({
773 "pickle":{
774 "initial_ratchet":{
775 "inner": expected_inner,
776 "counter":0
777 },
778 "signing_key":[
779 213,161,95,135,114,153,162,127,217,74,64,2,59,143,93,5,190,157,120,
780 80,89,8,87,129,115,148,104,144,152,186,178,109
781 ],
782 "signing_key_verified":true,
783 "config":{"version":"V1"}
784 },
785 "sender_key":"AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8",
786 "signing_key":{"ed25519":"wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww"},
787 "sender_data":{
788 "UnknownDevice":{
789 "legacy_session":false
790 }
791 },
792 "room_id":"!test:localhost",
793 "imported":false,
794 "backed_up":false,
795 "history_visibility":"shared",
796 "algorithm":"m.megolm.v1.aes-sha2"
797 })
798 );
799 }
800
801 #[async_test]
802 async fn test_can_deserialise_pickled_session_with_sender_data() {
803 let pickle = r#"
805 {
806 "pickle": {
807 "initial_ratchet": {
808 "inner": [ 124, 251, 213, 204, 108, 247, 54, 7, 179, 162, 15, 107, 154, 215,
809 220, 46, 123, 113, 120, 162, 225, 246, 237, 203, 125, 102, 190, 212,
810 229, 195, 136, 185, 26, 31, 77, 140, 144, 181, 152, 177, 46, 105,
811 202, 6, 53, 158, 157, 170, 31, 155, 130, 87, 214, 110, 143, 55, 68,
812 138, 41, 35, 242, 230, 194, 15, 16, 145, 116, 94, 89, 35, 79, 145,
813 245, 117, 204, 173, 166, 178, 49, 131, 143, 61, 61, 15, 211, 167, 17,
814 2, 79, 110, 149, 200, 223, 23, 185, 200, 29, 64, 55, 39, 147, 167,
815 205, 224, 159, 101, 218, 249, 203, 30, 175, 174, 48, 252, 40, 131,
816 52, 135, 91, 57, 211, 96, 105, 58, 55, 68, 250, 24 ],
817 "counter": 0
818 },
819 "signing_key": [ 93, 185, 171, 61, 173, 100, 51, 9, 157, 180, 214, 39, 131, 80, 118,
820 130, 199, 232, 163, 197, 45, 23, 227, 100, 151, 59, 19, 102, 38,
821 149, 43, 38 ],
822 "signing_key_verified": true,
823 "config": {
824 "version": "V1"
825 }
826 },
827 "sender_key": "AmM1DvVJarsNNXVuX7OarzfT481N37GtDwvDVF0RcR8",
828 "signing_key": {
829 "ed25519": "wTRTdz4rn4EY+68cKPzpMdQ6RAlg7T8cbTmEjaXuUww"
830 },
831 "sender_data":{
832 "UnknownDevice":{
833 "legacy_session":false
834 }
835 },
836 "room_id": "!test:localhost",
837 "forwarding_chains": ["tb6kQKjk+SJl2KnfQ0lKVOZl6gDFMcsb9HcUP9k/4hc"],
838 "imported": false,
839 "backed_up": false,
840 "history_visibility": "shared",
841 "algorithm": "m.megolm.v1.aes-sha2"
842 }
843 "#;
844
845 let deserialized = serde_json::from_str(pickle).unwrap();
847
848 let unpickled = InboundGroupSession::from_pickle(deserialized).unwrap();
850
851 assert_eq!(unpickled.session_id(), "XbmrPa1kMwmdtNYng1B2gsfoo8UtF+NklzsTZiaVKyY");
853
854 assert_let!(
857 SenderData::UnknownDevice { legacy_session, owner_check_failed } =
858 unpickled.sender_data
859 );
860 assert!(!legacy_session);
861 assert!(!owner_check_failed);
862 }
863
864 #[async_test]
865 async fn test_session_comparison() {
866 let alice = Account::with_device_id(alice_id(), alice_device_id());
867 let room_id = room_id!("!test:localhost");
868
869 let (_, inbound) = alice.create_group_session_pair_with_defaults(room_id).await;
870
871 let worse = InboundGroupSession::from_export(&inbound.export_at_index(10).await).unwrap();
872 let mut copy = InboundGroupSession::from_pickle(inbound.pickle().await).unwrap();
873
874 assert_eq!(inbound.compare(&worse).await, SessionOrdering::Better);
875 assert_eq!(worse.compare(&inbound).await, SessionOrdering::Worse);
876 assert_eq!(inbound.compare(&inbound).await, SessionOrdering::Equal);
877 assert_eq!(inbound.compare(©).await, SessionOrdering::Equal);
878
879 copy.creator_info.curve25519_key =
880 Curve25519PublicKey::from_base64("XbmrPa1kMwmdtNYng1B2gsfoo8UtF+NklzsTZiaVKyY")
881 .unwrap();
882
883 assert_eq!(inbound.compare(©).await, SessionOrdering::Unconnected);
884 }
885
886 #[async_test]
887 async fn test_session_comparison_sender_data() {
888 let alice = Account::with_device_id(alice_id(), alice_device_id());
889 let room_id = room_id!("!test:localhost");
890
891 let (_, mut inbound) = alice.create_group_session_pair_with_defaults(room_id).await;
892
893 let sender_data = SenderData::SenderVerified(KnownSenderData {
894 user_id: alice.user_id().into(),
895 device_id: Some(alice.device_id().into()),
896 master_key: alice.identity_keys().ed25519.into(),
897 });
898
899 let mut better = InboundGroupSession::from_pickle(inbound.pickle().await).unwrap();
900 better.sender_data = sender_data.clone();
901
902 assert_eq!(inbound.compare(&better).await, SessionOrdering::Worse);
903 assert_eq!(better.compare(&inbound).await, SessionOrdering::Better);
904
905 inbound.sender_data = sender_data;
906 assert_eq!(better.compare(&inbound).await, SessionOrdering::Equal);
907 }
908
909 fn create_session_key() -> SessionKey {
910 SessionKey::from_base64(
911 "\
912 AgAAAADBy9+YIYTIqBjFT67nyi31gIOypZQl8day2hkhRDCZaHoG+cZh4tZLQIAZimJail0\
913 0zq4DVJVljO6cZ2t8kIto/QVk+7p20Fcf2nvqZyL2ZCda2Ei7VsqWZHTM/gqa2IU9+ktkwz\
914 +KFhENnHvDhG9f+hjsAPZd5mTTpdO+tVcqtdWhX4dymaJ/2UpAAjuPXQW+nXhQWQhXgXOUa\
915 JCYurJtvbCbqZGeDMmVIoqukBs2KugNJ6j5WlTPoeFnMl6Guy9uH2iWWxGg8ZgT2xspqVl5\
916 CwujjC+m7Dh1toVkvu+bAw\
917 ",
918 )
919 .unwrap()
920 }
921}