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