1use std::{collections::BTreeMap, fmt, sync::Arc};
16
17#[cfg(doc)]
18use ruma::events::AnyTimelineEvent;
19use ruma::{
20 events::{AnyMessageLikeEvent, AnySyncTimelineEvent, AnyToDeviceEvent},
21 push::Action,
22 serde::{
23 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
24 SerializeAsRefStr,
25 },
26 DeviceKeyAlgorithm, OwnedDeviceId, OwnedEventId, OwnedUserId,
27};
28use serde::{Deserialize, Serialize};
29use tracing::warn;
30#[cfg(target_family = "wasm")]
31use wasm_bindgen::prelude::*;
32
33use crate::{
34 debug::{DebugRawEvent, DebugStructExt},
35 serde_helpers::extract_bundled_thread_summary,
36};
37
38const AUTHENTICITY_NOT_GUARANTEED: &str =
39 "The authenticity of this encrypted message can't be guaranteed on this device.";
40const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
41const VERIFICATION_VIOLATION: &str =
42 "Encrypted by a previously-verified user who is no longer verified.";
43const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
44const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
45const MISMATCHED_SENDER: &str =
46 "The sender of the event does not match the owner of the device that created the Megolm session.";
47pub const SENT_IN_CLEAR: &str = "Not encrypted.";
48
49#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
52#[serde(from = "OldVerificationStateHelper")]
53pub enum VerificationState {
54 Verified,
59
60 Unverified(VerificationLevel),
65}
66
67#[derive(Clone, Debug, Deserialize)]
70enum OldVerificationStateHelper {
71 Untrusted,
72 UnknownDevice,
73 #[serde(alias = "Trusted")]
74 Verified,
75 Unverified(VerificationLevel),
76}
77
78impl From<OldVerificationStateHelper> for VerificationState {
79 fn from(value: OldVerificationStateHelper) -> Self {
80 match value {
81 OldVerificationStateHelper::Untrusted => {
84 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
85 }
86 OldVerificationStateHelper::UnknownDevice => {
87 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
88 }
89 OldVerificationStateHelper::Verified => Self::Verified,
90 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
91 }
92 }
93}
94
95impl VerificationState {
96 pub fn to_shield_state_strict(&self) -> ShieldState {
103 match self {
104 VerificationState::Verified => ShieldState::None,
105 VerificationState::Unverified(level) => match level {
106 VerificationLevel::UnverifiedIdentity
107 | VerificationLevel::VerificationViolation
108 | VerificationLevel::UnsignedDevice => ShieldState::Red {
109 code: ShieldStateCode::UnverifiedIdentity,
110 message: UNVERIFIED_IDENTITY,
111 },
112 VerificationLevel::None(link) => match link {
113 DeviceLinkProblem::MissingDevice => ShieldState::Red {
114 code: ShieldStateCode::UnknownDevice,
115 message: UNKNOWN_DEVICE,
116 },
117 DeviceLinkProblem::InsecureSource => ShieldState::Red {
118 code: ShieldStateCode::AuthenticityNotGuaranteed,
119 message: AUTHENTICITY_NOT_GUARANTEED,
120 },
121 },
122 VerificationLevel::MismatchedSender => ShieldState::Red {
123 code: ShieldStateCode::MismatchedSender,
124 message: MISMATCHED_SENDER,
125 },
126 },
127 }
128 }
129
130 pub fn to_shield_state_lax(&self) -> ShieldState {
138 match self {
139 VerificationState::Verified => ShieldState::None,
140 VerificationState::Unverified(level) => match level {
141 VerificationLevel::UnverifiedIdentity => {
142 ShieldState::None
145 }
146 VerificationLevel::VerificationViolation => {
147 ShieldState::Red {
150 code: ShieldStateCode::VerificationViolation,
151 message: VERIFICATION_VIOLATION,
152 }
153 }
154 VerificationLevel::UnsignedDevice => {
155 ShieldState::Red {
157 code: ShieldStateCode::UnsignedDevice,
158 message: UNSIGNED_DEVICE,
159 }
160 }
161 VerificationLevel::None(link) => match link {
162 DeviceLinkProblem::MissingDevice => {
163 ShieldState::Red {
167 code: ShieldStateCode::UnknownDevice,
168 message: UNKNOWN_DEVICE,
169 }
170 }
171 DeviceLinkProblem::InsecureSource => {
172 ShieldState::Grey {
175 code: ShieldStateCode::AuthenticityNotGuaranteed,
176 message: AUTHENTICITY_NOT_GUARANTEED,
177 }
178 }
179 },
180 VerificationLevel::MismatchedSender => ShieldState::Red {
181 code: ShieldStateCode::MismatchedSender,
182 message: MISMATCHED_SENDER,
183 },
184 },
185 }
186 }
187}
188
189#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
192pub enum VerificationLevel {
193 UnverifiedIdentity,
195
196 #[serde(alias = "PreviouslyVerified")]
199 VerificationViolation,
200
201 UnsignedDevice,
204
205 None(DeviceLinkProblem),
211
212 MismatchedSender,
215}
216
217impl fmt::Display for VerificationLevel {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
219 let display = match self {
220 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
221 VerificationLevel::VerificationViolation => {
222 "The sender's identity was previously verified but has changed"
223 }
224 VerificationLevel::UnsignedDevice => {
225 "The sending device was not signed by the user's identity"
226 }
227 VerificationLevel::None(..) => "The sending device is not known",
228 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
229 };
230 write!(f, "{display}")
231 }
232}
233
234#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
237pub enum DeviceLinkProblem {
238 MissingDevice,
242 InsecureSource,
245}
246
247#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
250pub enum ShieldState {
251 Red {
254 code: ShieldStateCode,
256 message: &'static str,
258 },
259 Grey {
262 code: ShieldStateCode,
264 message: &'static str,
266 },
267 None,
269}
270
271#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
273#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
274#[cfg_attr(target_family = "wasm", wasm_bindgen)]
275pub enum ShieldStateCode {
276 AuthenticityNotGuaranteed,
278 UnknownDevice,
280 UnsignedDevice,
282 UnverifiedIdentity,
284 SentInClear,
286 #[serde(alias = "PreviouslyVerified")]
288 VerificationViolation,
289 MismatchedSender,
292}
293
294#[derive(Clone, Debug, Deserialize, Serialize)]
296pub enum AlgorithmInfo {
297 MegolmV1AesSha2 {
299 curve25519_key: String,
302 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
306
307 #[serde(default, skip_serializing_if = "Option::is_none")]
310 session_id: Option<String>,
311 },
312
313 OlmV1Curve25519AesSha2 {
315 curve25519_public_key_base64: String,
317 },
318}
319
320#[derive(Clone, Debug, Serialize)]
322pub struct EncryptionInfo {
323 pub sender: OwnedUserId,
326 pub sender_device: Option<OwnedDeviceId>,
329 pub algorithm_info: AlgorithmInfo,
331 pub verification_state: VerificationState,
338}
339
340impl EncryptionInfo {
341 pub fn session_id(&self) -> Option<&str> {
343 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
344 session_id.as_deref()
345 } else {
346 None
347 }
348 }
349}
350
351impl<'de> Deserialize<'de> for EncryptionInfo {
352 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
353 where
354 D: serde::Deserializer<'de>,
355 {
356 #[derive(Deserialize)]
359 struct Helper {
360 pub sender: OwnedUserId,
361 pub sender_device: Option<OwnedDeviceId>,
362 pub algorithm_info: AlgorithmInfo,
363 pub verification_state: VerificationState,
364 #[serde(rename = "session_id")]
365 pub old_session_id: Option<String>,
366 }
367
368 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
369 Helper::deserialize(deserializer)?;
370
371 let algorithm_info = match algorithm_info {
372 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
373 AlgorithmInfo::MegolmV1AesSha2 {
374 session_id: session_id.or(old_session_id),
376 curve25519_key,
377 sender_claimed_keys,
378 }
379 }
380 other => other,
381 };
382
383 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
384 }
385}
386
387#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
398pub struct ThreadSummary {
399 #[serde(skip_serializing_if = "Option::is_none")]
401 pub latest_reply: Option<OwnedEventId>,
402
403 pub num_replies: u32,
409}
410
411#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
413pub enum ThreadSummaryStatus {
414 #[default]
416 Unknown,
417 None,
419 Some(ThreadSummary),
421}
422
423impl ThreadSummaryStatus {
424 fn is_unknown(&self) -> bool {
426 matches!(self, ThreadSummaryStatus::Unknown)
427 }
428
429 pub fn summary(&self) -> Option<&ThreadSummary> {
432 match self {
433 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
434 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
435 }
436 }
437}
438
439#[derive(Clone, Debug, Serialize)]
462pub struct TimelineEvent {
463 pub kind: TimelineEventKind,
465
466 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
471 push_actions: Option<Vec<Action>>,
472
473 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
475 pub thread_summary: ThreadSummaryStatus,
476
477 #[serde(skip)]
482 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
483}
484
485fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
487 push_actions.as_ref().is_none_or(|v| v.is_empty())
488}
489
490#[cfg(not(feature = "test-send-sync"))]
492unsafe impl Send for TimelineEvent {}
493
494#[cfg(not(feature = "test-send-sync"))]
496unsafe impl Sync for TimelineEvent {}
497
498#[cfg(feature = "test-send-sync")]
499#[test]
500fn test_send_sync_for_sync_timeline_event() {
502 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
503
504 assert_send_sync::<TimelineEvent>();
505}
506
507impl TimelineEvent {
508 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
513 Self::new(TimelineEventKind::PlainText { event }, None)
514 }
515
516 pub fn from_decrypted(
518 decrypted: DecryptedRoomEvent,
519 push_actions: Option<Vec<Action>>,
520 ) -> Self {
521 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions)
522 }
523
524 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
527 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None)
528 }
529
530 fn new(kind: TimelineEventKind, push_actions: Option<Vec<Action>>) -> Self {
533 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(kind.raw());
534 let bundled_latest_thread_event =
535 Self::from_bundled_latest_event(&kind, latest_thread_event);
536 Self { kind, push_actions, thread_summary, bundled_latest_thread_event }
537 }
538
539 fn from_bundled_latest_event(
543 this: &TimelineEventKind,
544 latest_event: Option<Raw<AnyMessageLikeEvent>>,
545 ) -> Option<Box<Self>> {
546 let latest_event = latest_event?;
547
548 match this {
549 TimelineEventKind::Decrypted(decrypted) => {
550 if let Some(unsigned_decryption_result) =
551 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
552 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
553 })
554 {
555 match unsigned_decryption_result {
556 UnsignedDecryptionResult::Decrypted(encryption_info) => {
557 return Some(Box::new(TimelineEvent::from_decrypted(
560 DecryptedRoomEvent {
561 event: latest_event,
562 encryption_info: encryption_info.clone(),
563 unsigned_encryption_info: None,
567 },
568 None,
569 )));
570 }
571
572 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
573 return Some(Box::new(TimelineEvent::from_utd(
575 latest_event.cast(),
576 utd_info.clone(),
577 )));
578 }
579 }
580 }
581 }
582
583 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
584 }
586 }
587
588 let deserialized = match latest_event.deserialize() {
589 Ok(ev) => ev,
590 Err(err) => {
591 warn!("couldn't deserialize bundled latest thread event: {err}");
592 return None;
593 }
594 };
595
596 match deserialized {
597 AnyMessageLikeEvent::RoomEncrypted(_) => {
598 Some(Box::new(TimelineEvent::from_utd(
602 latest_event.cast(),
603 UnableToDecryptInfo {
604 session_id: None,
605 reason: UnableToDecryptReason::Unknown,
606 },
607 )))
608 }
609
610 _ => Some(Box::new(TimelineEvent::from_plaintext(latest_event.cast()))),
611 }
612 }
613
614 pub fn push_actions(&self) -> Option<&[Action]> {
619 self.push_actions.as_deref()
620 }
621
622 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
624 self.push_actions = Some(push_actions);
625 }
626
627 pub fn event_id(&self) -> Option<OwnedEventId> {
630 self.kind.event_id()
631 }
632
633 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
636 self.kind.raw()
637 }
638
639 pub fn replace_raw(&mut self, replacement: Raw<AnyMessageLikeEvent>) {
641 match &mut self.kind {
642 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
643 TimelineEventKind::UnableToDecrypt { event, .. }
644 | TimelineEventKind::PlainText { event } => {
645 *event = replacement.cast();
648 }
649 }
650 }
651
652 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
655 self.kind.encryption_info()
656 }
657
658 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
661 self.kind.into_raw()
662 }
663}
664
665impl<'de> Deserialize<'de> for TimelineEvent {
666 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
674 where
675 D: serde::Deserializer<'de>,
676 {
677 use serde_json::{Map, Value};
678
679 let value = Map::<String, Value>::deserialize(deserializer)?;
681
682 if value.contains_key("event") {
684 let v0: SyncTimelineEventDeserializationHelperV0 =
685 serde_json::from_value(Value::Object(value)).map_err(|e| {
686 serde::de::Error::custom(format!(
687 "Unable to deserialize V0-format TimelineEvent: {e}",
688 ))
689 })?;
690 Ok(v0.into())
691 }
692 else {
694 let v1: SyncTimelineEventDeserializationHelperV1 =
695 serde_json::from_value(Value::Object(value)).map_err(|e| {
696 serde::de::Error::custom(format!(
697 "Unable to deserialize V1-format TimelineEvent: {e}",
698 ))
699 })?;
700 Ok(v1.into())
701 }
702 }
703}
704
705#[derive(Clone, Serialize, Deserialize)]
707pub enum TimelineEventKind {
708 Decrypted(DecryptedRoomEvent),
710
711 UnableToDecrypt {
713 event: Raw<AnySyncTimelineEvent>,
717
718 utd_info: UnableToDecryptInfo,
720 },
721
722 PlainText {
724 event: Raw<AnySyncTimelineEvent>,
728 },
729}
730
731impl TimelineEventKind {
732 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
735 match self {
736 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
742 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast_ref(),
743 TimelineEventKind::PlainText { event } => event,
744 }
745 }
746
747 pub fn event_id(&self) -> Option<OwnedEventId> {
750 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
751 }
752
753 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
756 match self {
757 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
758 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
759 }
760 }
761
762 pub fn unsigned_encryption_map(
765 &self,
766 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
767 match self {
768 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
769 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
770 }
771 }
772
773 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
776 match self {
777 TimelineEventKind::Decrypted(d) => d.event.cast(),
783 TimelineEventKind::UnableToDecrypt { event, .. } => event.cast(),
784 TimelineEventKind::PlainText { event } => event,
785 }
786 }
787
788 pub fn session_id(&self) -> Option<&str> {
791 match self {
792 TimelineEventKind::Decrypted(decrypted_room_event) => {
793 decrypted_room_event.encryption_info.session_id()
794 }
795 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
796 TimelineEventKind::PlainText { .. } => None,
797 }
798 }
799}
800
801#[cfg(not(tarpaulin_include))]
802impl fmt::Debug for TimelineEventKind {
803 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804 match &self {
805 Self::PlainText { event } => f
806 .debug_struct("TimelineEventDecryptionResult::PlainText")
807 .field("event", &DebugRawEvent(event))
808 .finish(),
809
810 Self::UnableToDecrypt { event, utd_info } => f
811 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
812 .field("event", &DebugRawEvent(event))
813 .field("utd_info", &utd_info)
814 .finish(),
815
816 Self::Decrypted(decrypted) => {
817 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
818 }
819 }
820 }
821}
822
823#[derive(Clone, Serialize, Deserialize)]
824pub struct DecryptedRoomEvent {
826 pub event: Raw<AnyMessageLikeEvent>,
833
834 pub encryption_info: Arc<EncryptionInfo>,
836
837 #[serde(skip_serializing_if = "Option::is_none")]
842 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
843}
844
845#[cfg(not(tarpaulin_include))]
846impl fmt::Debug for DecryptedRoomEvent {
847 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
848 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
849
850 f.debug_struct("DecryptedRoomEvent")
851 .field("event", &DebugRawEvent(event))
852 .field("encryption_info", encryption_info)
853 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
854 .finish()
855 }
856}
857
858#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
860pub enum UnsignedEventLocation {
861 RelationsReplace,
864 RelationsThreadLatestEvent,
867}
868
869impl UnsignedEventLocation {
870 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
877 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
878
879 match self {
880 Self::RelationsReplace => relations.get_mut("m.replace"),
881 Self::RelationsThreadLatestEvent => {
882 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
883 }
884 }
885 }
886}
887
888#[derive(Debug, Clone, Serialize, Deserialize)]
890pub enum UnsignedDecryptionResult {
891 Decrypted(Arc<EncryptionInfo>),
893 UnableToDecrypt(UnableToDecryptInfo),
895}
896
897impl UnsignedDecryptionResult {
898 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
901 match self {
902 Self::Decrypted(info) => Some(info),
903 Self::UnableToDecrypt(_) => None,
904 }
905 }
906}
907
908#[derive(Debug, Clone, Serialize, Deserialize)]
910pub struct UnableToDecryptInfo {
911 #[serde(skip_serializing_if = "Option::is_none")]
914 pub session_id: Option<String>,
915
916 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
918 pub reason: UnableToDecryptReason,
919}
920
921fn unknown_utd_reason() -> UnableToDecryptReason {
922 UnableToDecryptReason::Unknown
923}
924
925pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
928where
929 D: serde::Deserializer<'de>,
930{
931 let v: serde_json::Value = Deserialize::deserialize(d)?;
933 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
936 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
937 }
938 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
941}
942
943#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
945pub enum UnableToDecryptReason {
946 #[doc(hidden)]
949 Unknown,
950
951 MalformedEncryptedEvent,
955
956 MissingMegolmSession {
959 withheld_code: Option<WithheldCode>,
962 },
963
964 UnknownMegolmMessageIndex,
967
968 MegolmDecryptionFailure,
975
976 PayloadDeserializationFailure,
978
979 MismatchedIdentityKeys,
983
984 SenderIdentityNotTrusted(VerificationLevel),
988}
989
990impl UnableToDecryptReason {
991 pub fn is_missing_room_key(&self) -> bool {
994 matches!(
997 self,
998 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
999 )
1000 }
1001}
1002
1003#[derive(
1007 Clone,
1008 PartialEq,
1009 Eq,
1010 Hash,
1011 AsStrAsRefStr,
1012 AsRefStr,
1013 FromString,
1014 DebugAsRefStr,
1015 SerializeAsRefStr,
1016 DeserializeFromCowStr,
1017)]
1018pub enum WithheldCode {
1019 #[ruma_enum(rename = "m.blacklisted")]
1021 Blacklisted,
1022
1023 #[ruma_enum(rename = "m.unverified")]
1025 Unverified,
1026
1027 #[ruma_enum(rename = "m.unauthorised")]
1031 Unauthorised,
1032
1033 #[ruma_enum(rename = "m.unavailable")]
1036 Unavailable,
1037
1038 #[ruma_enum(rename = "m.no_olm")]
1042 NoOlm,
1043
1044 #[doc(hidden)]
1045 _Custom(PrivOwnedStr),
1046}
1047
1048impl fmt::Display for WithheldCode {
1049 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1050 let string = match self {
1051 WithheldCode::Blacklisted => "The sender has blocked you.",
1052 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1053 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1054 WithheldCode::Unavailable => "The requested key was not found.",
1055 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1056 _ => self.as_str(),
1057 };
1058
1059 f.write_str(string)
1060 }
1061}
1062
1063#[doc(hidden)]
1067#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1068pub struct PrivOwnedStr(pub Box<str>);
1069
1070#[cfg(not(tarpaulin_include))]
1071impl fmt::Debug for PrivOwnedStr {
1072 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1073 self.0.fmt(f)
1074 }
1075}
1076
1077#[derive(Debug, Deserialize)]
1082struct SyncTimelineEventDeserializationHelperV1 {
1083 kind: TimelineEventKind,
1085
1086 #[serde(default)]
1088 push_actions: Vec<Action>,
1089
1090 #[serde(default)]
1092 thread_summary: ThreadSummaryStatus,
1093}
1094
1095impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1096 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1097 let SyncTimelineEventDeserializationHelperV1 { kind, push_actions, thread_summary } = value;
1098 TimelineEvent {
1099 kind,
1100 push_actions: Some(push_actions),
1101 thread_summary,
1102 bundled_latest_thread_event: None,
1104 }
1105 }
1106}
1107
1108#[derive(Deserialize)]
1110struct SyncTimelineEventDeserializationHelperV0 {
1111 event: Raw<AnySyncTimelineEvent>,
1113
1114 encryption_info: Option<Arc<EncryptionInfo>>,
1118
1119 #[serde(default)]
1121 push_actions: Vec<Action>,
1122
1123 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1128}
1129
1130impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1131 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1132 let SyncTimelineEventDeserializationHelperV0 {
1133 event,
1134 encryption_info,
1135 push_actions,
1136 unsigned_encryption_info,
1137 } = value;
1138
1139 let kind = match encryption_info {
1140 Some(encryption_info) => {
1141 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1142 event: event.cast(),
1149 encryption_info,
1150 unsigned_encryption_info,
1151 })
1152 }
1153
1154 None => TimelineEventKind::PlainText { event },
1155 };
1156
1157 TimelineEvent {
1158 kind,
1159 push_actions: Some(push_actions),
1160 thread_summary: ThreadSummaryStatus::Unknown,
1162 bundled_latest_thread_event: None,
1164 }
1165 }
1166}
1167
1168#[derive(Clone, Debug)]
1170pub enum ProcessedToDeviceEvent {
1171 Decrypted {
1174 raw: Raw<AnyToDeviceEvent>,
1176 encryption_info: EncryptionInfo,
1178 },
1179
1180 UnableToDecrypt(Raw<AnyToDeviceEvent>),
1182
1183 PlainText(Raw<AnyToDeviceEvent>),
1185
1186 Invalid(Raw<AnyToDeviceEvent>),
1190}
1191
1192impl ProcessedToDeviceEvent {
1193 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1196 match self {
1197 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1198 ProcessedToDeviceEvent::UnableToDecrypt(event) => event.clone(),
1199 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1200 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1201 }
1202 }
1203
1204 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1206 match self {
1207 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1208 ProcessedToDeviceEvent::UnableToDecrypt(event) => event,
1209 ProcessedToDeviceEvent::PlainText(event) => event,
1210 ProcessedToDeviceEvent::Invalid(event) => event,
1211 }
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use std::{collections::BTreeMap, sync::Arc};
1218
1219 use assert_matches::assert_matches;
1220 use assert_matches2::assert_let;
1221 use insta::{assert_json_snapshot, with_settings};
1222 use ruma::{
1223 device_id, event_id, events::room::message::RoomMessageEventContent, serde::Raw, user_id,
1224 DeviceKeyAlgorithm,
1225 };
1226 use serde::Deserialize;
1227 use serde_json::json;
1228
1229 use super::{
1230 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1231 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1232 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1233 VerificationState, WithheldCode,
1234 };
1235 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1236
1237 fn example_event() -> serde_json::Value {
1238 json!({
1239 "content": RoomMessageEventContent::text_plain("secret"),
1240 "type": "m.room.message",
1241 "event_id": "$xxxxx:example.org",
1242 "room_id": "!someroom:example.com",
1243 "origin_server_ts": 2189,
1244 "sender": "@carl:example.com",
1245 })
1246 }
1247
1248 #[test]
1249 fn sync_timeline_debug_content() {
1250 let room_event = TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast());
1251 let debug_s = format!("{room_event:?}");
1252 assert!(
1253 !debug_s.contains("secret"),
1254 "Debug representation contains event content!\n{debug_s}"
1255 );
1256 }
1257
1258 #[test]
1259 fn old_verification_state_to_new_migration() {
1260 #[derive(Deserialize)]
1261 struct State {
1262 state: VerificationState,
1263 }
1264
1265 let state = json!({
1266 "state": "Trusted",
1267 });
1268 let deserialized: State =
1269 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1270 assert_eq!(deserialized.state, VerificationState::Verified);
1271
1272 let state = json!({
1273 "state": "UnknownDevice",
1274 });
1275
1276 let deserialized: State =
1277 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1278
1279 assert_eq!(
1280 deserialized.state,
1281 VerificationState::Unverified(VerificationLevel::None(
1282 DeviceLinkProblem::MissingDevice
1283 ))
1284 );
1285
1286 let state = json!({
1287 "state": "Untrusted",
1288 });
1289 let deserialized: State =
1290 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1291
1292 assert_eq!(
1293 deserialized.state,
1294 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1295 );
1296 }
1297
1298 #[test]
1299 fn test_verification_level_deserializes() {
1300 #[derive(Deserialize)]
1302 struct Container {
1303 verification_level: VerificationLevel,
1304 }
1305 let container = json!({ "verification_level": "VerificationViolation" });
1306
1307 let deserialized: Container = serde_json::from_value(container)
1309 .expect("We can deserialize the old PreviouslyVerified value");
1310
1311 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1313 }
1314
1315 #[test]
1316 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1317 #[derive(Deserialize)]
1319 struct Container {
1320 verification_level: VerificationLevel,
1321 }
1322 let container = json!({ "verification_level": "PreviouslyVerified" });
1323
1324 let deserialized: Container = serde_json::from_value(container)
1326 .expect("We can deserialize the old PreviouslyVerified value");
1327
1328 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1330 }
1331
1332 #[test]
1333 fn test_shield_state_code_deserializes() {
1334 #[derive(Deserialize)]
1336 struct Container {
1337 shield_state_code: ShieldStateCode,
1338 }
1339 let container = json!({ "shield_state_code": "VerificationViolation" });
1340
1341 let deserialized: Container = serde_json::from_value(container)
1343 .expect("We can deserialize the old PreviouslyVerified value");
1344
1345 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1347 }
1348
1349 #[test]
1350 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1351 #[derive(Deserialize)]
1353 struct Container {
1354 shield_state_code: ShieldStateCode,
1355 }
1356 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1357
1358 let deserialized: Container = serde_json::from_value(container)
1360 .expect("We can deserialize the old PreviouslyVerified value");
1361
1362 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1364 }
1365
1366 #[test]
1367 fn sync_timeline_event_serialisation() {
1368 let room_event = TimelineEvent {
1369 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1370 event: Raw::new(&example_event()).unwrap().cast(),
1371 encryption_info: Arc::new(EncryptionInfo {
1372 sender: user_id!("@sender:example.com").to_owned(),
1373 sender_device: None,
1374 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1375 curve25519_key: "xxx".to_owned(),
1376 sender_claimed_keys: Default::default(),
1377 session_id: Some("xyz".to_owned()),
1378 },
1379 verification_state: VerificationState::Verified,
1380 }),
1381 unsigned_encryption_info: Some(BTreeMap::from([(
1382 UnsignedEventLocation::RelationsReplace,
1383 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1384 session_id: Some("xyz".to_owned()),
1385 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1386 }),
1387 )])),
1388 }),
1389 push_actions: Default::default(),
1390 thread_summary: ThreadSummaryStatus::Unknown,
1391 bundled_latest_thread_event: None,
1392 };
1393
1394 let serialized = serde_json::to_value(&room_event).unwrap();
1395
1396 assert_eq!(
1398 serialized,
1399 json!({
1400 "kind": {
1401 "Decrypted": {
1402 "event": {
1403 "content": {"body": "secret", "msgtype": "m.text"},
1404 "event_id": "$xxxxx:example.org",
1405 "origin_server_ts": 2189,
1406 "room_id": "!someroom:example.com",
1407 "sender": "@carl:example.com",
1408 "type": "m.room.message",
1409 },
1410 "encryption_info": {
1411 "sender": "@sender:example.com",
1412 "sender_device": null,
1413 "algorithm_info": {
1414 "MegolmV1AesSha2": {
1415 "curve25519_key": "xxx",
1416 "sender_claimed_keys": {},
1417 "session_id": "xyz",
1418 }
1419 },
1420 "verification_state": "Verified",
1421 },
1422 "unsigned_encryption_info": {
1423 "RelationsReplace": {"UnableToDecrypt": {
1424 "session_id": "xyz",
1425 "reason": "MalformedEncryptedEvent",
1426 }}
1427 }
1428 }
1429 }
1430 })
1431 );
1432
1433 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1435 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1436 assert_matches!(
1437 event.encryption_info().unwrap().algorithm_info,
1438 AlgorithmInfo::MegolmV1AesSha2 { .. }
1439 );
1440
1441 let serialized = json!({
1443 "event": {
1444 "content": {"body": "secret", "msgtype": "m.text"},
1445 "event_id": "$xxxxx:example.org",
1446 "origin_server_ts": 2189,
1447 "room_id": "!someroom:example.com",
1448 "sender": "@carl:example.com",
1449 "type": "m.room.message",
1450 },
1451 "encryption_info": {
1452 "sender": "@sender:example.com",
1453 "sender_device": null,
1454 "algorithm_info": {
1455 "MegolmV1AesSha2": {
1456 "curve25519_key": "xxx",
1457 "sender_claimed_keys": {}
1458 }
1459 },
1460 "verification_state": "Verified",
1461 },
1462 });
1463 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1464 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1465 assert_matches!(
1466 event.encryption_info().unwrap().algorithm_info,
1467 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1468 );
1469
1470 let serialized = json!({
1473 "event": {
1474 "content": {"body": "secret", "msgtype": "m.text"},
1475 "event_id": "$xxxxx:example.org",
1476 "origin_server_ts": 2189,
1477 "room_id": "!someroom:example.com",
1478 "sender": "@carl:example.com",
1479 "type": "m.room.message",
1480 },
1481 "encryption_info": {
1482 "sender": "@sender:example.com",
1483 "sender_device": null,
1484 "algorithm_info": {
1485 "MegolmV1AesSha2": {
1486 "curve25519_key": "xxx",
1487 "sender_claimed_keys": {}
1488 }
1489 },
1490 "verification_state": "Verified",
1491 },
1492 "unsigned_encryption_info": {
1493 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1494 }
1495 });
1496 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1497 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1498 assert_matches!(
1499 event.encryption_info().unwrap().algorithm_info,
1500 AlgorithmInfo::MegolmV1AesSha2 { .. }
1501 );
1502 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1503 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1504 assert_eq!(map.len(), 1);
1505 let (location, result) = map.into_iter().next().unwrap();
1506 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1507 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1508 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1509 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1510 })
1511 });
1512 });
1513 }
1514
1515 #[test]
1516 fn test_creating_or_deserializing_an_event_extracts_summary() {
1517 let event = json!({
1518 "event_id": "$eid:example.com",
1519 "type": "m.room.message",
1520 "sender": "@alice:example.com",
1521 "origin_server_ts": 42,
1522 "content": {
1523 "body": "Hello, world!",
1524 },
1525 "unsigned": {
1526 "m.relations": {
1527 "m.thread": {
1528 "latest_event": {
1529 "event_id": "$latest_event:example.com",
1530 "type": "m.room.message",
1531 "sender": "@bob:example.com",
1532 "origin_server_ts": 42,
1533 "content": {
1534 "body": "Hello to you too!",
1535 }
1536 },
1537 "count": 2,
1538 "current_user_participated": true,
1539 }
1540 }
1541 }
1542 });
1543
1544 let raw = Raw::new(&event).unwrap().cast();
1545
1546 let timeline_event = TimelineEvent::from_plaintext(raw);
1549 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1550 assert_eq!(num_replies, 2);
1551 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1552 });
1553
1554 let serialized_timeline_item = json!({
1557 "kind": {
1558 "PlainText": {
1559 "event": event
1560 }
1561 }
1562 });
1563
1564 let timeline_event: TimelineEvent =
1565 serde_json::from_value(serialized_timeline_item).unwrap();
1566 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1567 }
1568
1569 #[test]
1570 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1571 let serialized = json!({
1588 "kind": {
1589 "UnableToDecrypt": {
1590 "event": {
1591 "content": {
1592 "algorithm": "m.megolm.v1.aes-sha2",
1593 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1594 "device_id": "SKCGPNUWAU",
1595 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1596 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1597 },
1598 "event_id": "$xxxxx:example.org",
1599 "origin_server_ts": 2189,
1600 "room_id": "!someroom:example.com",
1601 "sender": "@carl:example.com",
1602 "type": "m.room.message"
1603 },
1604 "utd_info": {
1605 "reason": "MissingMegolmSession",
1606 "session_id": "session000"
1607 }
1608 }
1609 }
1610 });
1611
1612 let result = serde_json::from_value(serialized);
1613 assert!(result.is_ok());
1614
1615 let event: TimelineEvent = result.unwrap();
1617 assert_matches!(
1618 event.kind,
1619 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1620 assert_matches!(
1621 utd_info.reason,
1622 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1623 );
1624 }
1625 )
1626 }
1627
1628 #[test]
1629 fn unable_to_decrypt_info_migration_for_withheld() {
1630 let old_format = json!({
1631 "reason": "MissingMegolmSession",
1632 "session_id": "session000"
1633 });
1634
1635 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1636 let session_id = Some("session000".to_owned());
1637
1638 assert_eq!(deserialized.session_id, session_id);
1639 assert_eq!(
1640 deserialized.reason,
1641 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1642 );
1643
1644 let new_format = json!({
1645 "session_id": "session000",
1646 "reason": {
1647 "MissingMegolmSession": {
1648 "withheld_code": null
1649 }
1650 }
1651 });
1652
1653 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1654
1655 assert_eq!(
1656 deserialized.reason,
1657 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1658 );
1659 assert_eq!(deserialized.session_id, session_id);
1660 }
1661
1662 #[test]
1663 fn unable_to_decrypt_reason_is_missing_room_key() {
1664 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1665 assert!(reason.is_missing_room_key());
1666
1667 let reason = UnableToDecryptReason::MissingMegolmSession {
1668 withheld_code: Some(WithheldCode::Blacklisted),
1669 };
1670 assert!(!reason.is_missing_room_key());
1671
1672 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1673 assert!(reason.is_missing_room_key());
1674 }
1675
1676 #[test]
1677 fn snapshot_test_verification_level() {
1678 with_settings!({ prepend_module_to_snapshot => false }, {
1679 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1680 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1681 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1682 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1683 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1684 });
1685 }
1686
1687 #[test]
1688 fn snapshot_test_verification_states() {
1689 with_settings!({ prepend_module_to_snapshot => false }, {
1690 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1691 assert_json_snapshot!(VerificationState::Unverified(
1692 VerificationLevel::VerificationViolation
1693 ));
1694 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1695 DeviceLinkProblem::InsecureSource,
1696 )));
1697 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1698 DeviceLinkProblem::MissingDevice,
1699 )));
1700 assert_json_snapshot!(VerificationState::Verified);
1701 });
1702 }
1703
1704 #[test]
1705 fn snapshot_test_shield_states() {
1706 with_settings!({ prepend_module_to_snapshot => false }, {
1707 assert_json_snapshot!(ShieldState::None);
1708 assert_json_snapshot!(ShieldState::Red {
1709 code: ShieldStateCode::UnverifiedIdentity,
1710 message: "a message"
1711 });
1712 assert_json_snapshot!(ShieldState::Grey {
1713 code: ShieldStateCode::AuthenticityNotGuaranteed,
1714 message: "authenticity of this message cannot be guaranteed",
1715 });
1716 });
1717 }
1718
1719 #[test]
1720 fn snapshot_test_shield_codes() {
1721 with_settings!({ prepend_module_to_snapshot => false }, {
1722 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1723 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1724 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1725 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1726 assert_json_snapshot!(ShieldStateCode::SentInClear);
1727 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1728 });
1729 }
1730
1731 #[test]
1732 fn snapshot_test_algorithm_info() {
1733 let mut map = BTreeMap::new();
1734 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1735 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1736 let info = AlgorithmInfo::MegolmV1AesSha2 {
1737 curve25519_key: "curvecurvecurve".into(),
1738 sender_claimed_keys: BTreeMap::from([
1739 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1740 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1741 ]),
1742 session_id: None,
1743 };
1744
1745 with_settings!({ prepend_module_to_snapshot => false }, {
1746 assert_json_snapshot!(info)
1747 });
1748 }
1749
1750 #[test]
1751 fn test_encryption_info_migration() {
1752 let old_format = json!({
1755 "sender": "@alice:localhost",
1756 "sender_device": "ABCDEFGH",
1757 "algorithm_info": {
1758 "MegolmV1AesSha2": {
1759 "curve25519_key": "curvecurvecurve",
1760 "sender_claimed_keys": {}
1761 }
1762 },
1763 "verification_state": "Verified",
1764 "session_id": "mysessionid76"
1765 });
1766
1767 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1768 let expected_session_id = Some("mysessionid76".to_owned());
1769
1770 assert_let!(
1771 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
1772 );
1773 assert_eq!(session_id, expected_session_id);
1774
1775 assert_json_snapshot!(deserialized);
1776 }
1777
1778 #[test]
1779 fn snapshot_test_encryption_info() {
1780 let info = EncryptionInfo {
1781 sender: user_id!("@alice:localhost").to_owned(),
1782 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
1783 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1784 curve25519_key: "curvecurvecurve".into(),
1785 sender_claimed_keys: Default::default(),
1786 session_id: Some("mysessionid76".to_owned()),
1787 },
1788 verification_state: VerificationState::Verified,
1789 };
1790
1791 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1792 assert_json_snapshot!(info)
1793 })
1794 }
1795
1796 #[test]
1797 fn snapshot_test_sync_timeline_event() {
1798 let room_event = TimelineEvent {
1799 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1800 event: Raw::new(&example_event()).unwrap().cast(),
1801 encryption_info: Arc::new(EncryptionInfo {
1802 sender: user_id!("@sender:example.com").to_owned(),
1803 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
1804 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1805 curve25519_key: "xxx".to_owned(),
1806 sender_claimed_keys: BTreeMap::from([
1807 (
1808 DeviceKeyAlgorithm::Ed25519,
1809 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
1810 ),
1811 (
1812 DeviceKeyAlgorithm::Curve25519,
1813 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
1814 ),
1815 ]),
1816 session_id: Some("mysessionid112".to_owned()),
1817 },
1818 verification_state: VerificationState::Verified,
1819 }),
1820 unsigned_encryption_info: Some(BTreeMap::from([(
1821 UnsignedEventLocation::RelationsThreadLatestEvent,
1822 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1823 session_id: Some("xyz".to_owned()),
1824 reason: UnableToDecryptReason::MissingMegolmSession {
1825 withheld_code: Some(WithheldCode::Unverified),
1826 },
1827 }),
1828 )])),
1829 }),
1830 push_actions: Default::default(),
1831 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
1832 num_replies: 2,
1833 latest_reply: None,
1834 }),
1835 bundled_latest_thread_event: None,
1836 };
1837
1838 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
1839 assert_json_snapshot! {
1842 serde_json::to_value(&room_event).unwrap(),
1843 }
1844 });
1845 }
1846}