1use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedUserId,
19 events::{
20 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
21 MessageLikeEventType,
22 },
23 push::Action,
24 serde::{
25 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
26 SerializeAsRefStr,
27 },
28};
29use serde::{Deserialize, Serialize};
30use tracing::warn;
31#[cfg(target_family = "wasm")]
32use wasm_bindgen::prelude::*;
33
34use crate::{
35 debug::{DebugRawEvent, DebugStructExt},
36 serde_helpers::{extract_bundled_thread_summary, extract_timestamp},
37};
38
39const AUTHENTICITY_NOT_GUARANTEED: &str =
40 "The authenticity of this encrypted message can't be guaranteed on this device.";
41const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
42const VERIFICATION_VIOLATION: &str =
43 "Encrypted by a previously-verified user who is no longer verified.";
44const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
45const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
46const MISMATCHED_SENDER: &str = "\
47 The sender of the event does not match the owner of the device \
48 that created the Megolm session.";
49pub const SENT_IN_CLEAR: &str = "Not encrypted.";
50
51#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54#[serde(from = "OldVerificationStateHelper")]
55pub enum VerificationState {
56 Verified,
61
62 Unverified(VerificationLevel),
67}
68
69#[derive(Clone, Debug, Deserialize)]
72enum OldVerificationStateHelper {
73 Untrusted,
74 UnknownDevice,
75 #[serde(alias = "Trusted")]
76 Verified,
77 Unverified(VerificationLevel),
78}
79
80impl From<OldVerificationStateHelper> for VerificationState {
81 fn from(value: OldVerificationStateHelper) -> Self {
82 match value {
83 OldVerificationStateHelper::Untrusted => {
86 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
87 }
88 OldVerificationStateHelper::UnknownDevice => {
89 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
90 }
91 OldVerificationStateHelper::Verified => Self::Verified,
92 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
93 }
94 }
95}
96
97impl VerificationState {
98 pub fn to_shield_state_strict(&self) -> ShieldState {
105 match self {
106 VerificationState::Verified => ShieldState::None,
107 VerificationState::Unverified(level) => match level {
108 VerificationLevel::UnverifiedIdentity
109 | VerificationLevel::VerificationViolation
110 | VerificationLevel::UnsignedDevice => ShieldState::Red {
111 code: ShieldStateCode::UnverifiedIdentity,
112 message: UNVERIFIED_IDENTITY,
113 },
114 VerificationLevel::None(link) => match link {
115 DeviceLinkProblem::MissingDevice => ShieldState::Red {
116 code: ShieldStateCode::UnknownDevice,
117 message: UNKNOWN_DEVICE,
118 },
119 DeviceLinkProblem::InsecureSource => ShieldState::Red {
120 code: ShieldStateCode::AuthenticityNotGuaranteed,
121 message: AUTHENTICITY_NOT_GUARANTEED,
122 },
123 },
124 VerificationLevel::MismatchedSender => ShieldState::Red {
125 code: ShieldStateCode::MismatchedSender,
126 message: MISMATCHED_SENDER,
127 },
128 },
129 }
130 }
131
132 pub fn to_shield_state_lax(&self) -> ShieldState {
140 match self {
141 VerificationState::Verified => ShieldState::None,
142 VerificationState::Unverified(level) => match level {
143 VerificationLevel::UnverifiedIdentity => {
144 ShieldState::None
147 }
148 VerificationLevel::VerificationViolation => {
149 ShieldState::Red {
152 code: ShieldStateCode::VerificationViolation,
153 message: VERIFICATION_VIOLATION,
154 }
155 }
156 VerificationLevel::UnsignedDevice => {
157 ShieldState::Red {
159 code: ShieldStateCode::UnsignedDevice,
160 message: UNSIGNED_DEVICE,
161 }
162 }
163 VerificationLevel::None(link) => match link {
164 DeviceLinkProblem::MissingDevice => {
165 ShieldState::Red {
169 code: ShieldStateCode::UnknownDevice,
170 message: UNKNOWN_DEVICE,
171 }
172 }
173 DeviceLinkProblem::InsecureSource => {
174 ShieldState::Grey {
177 code: ShieldStateCode::AuthenticityNotGuaranteed,
178 message: AUTHENTICITY_NOT_GUARANTEED,
179 }
180 }
181 },
182 VerificationLevel::MismatchedSender => ShieldState::Red {
183 code: ShieldStateCode::MismatchedSender,
184 message: MISMATCHED_SENDER,
185 },
186 },
187 }
188 }
189}
190
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
194pub enum VerificationLevel {
195 UnverifiedIdentity,
197
198 #[serde(alias = "PreviouslyVerified")]
201 VerificationViolation,
202
203 UnsignedDevice,
206
207 None(DeviceLinkProblem),
213
214 MismatchedSender,
217}
218
219impl fmt::Display for VerificationLevel {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
221 let display = match self {
222 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
223 VerificationLevel::VerificationViolation => {
224 "The sender's identity was previously verified but has changed"
225 }
226 VerificationLevel::UnsignedDevice => {
227 "The sending device was not signed by the user's identity"
228 }
229 VerificationLevel::None(..) => "The sending device is not known",
230 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
231 };
232 write!(f, "{display}")
233 }
234}
235
236#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
239pub enum DeviceLinkProblem {
240 MissingDevice,
244 InsecureSource,
247}
248
249#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
252pub enum ShieldState {
253 Red {
256 code: ShieldStateCode,
258 message: &'static str,
260 },
261 Grey {
264 code: ShieldStateCode,
266 message: &'static str,
268 },
269 None,
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
275#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
276#[cfg_attr(target_family = "wasm", wasm_bindgen)]
277pub enum ShieldStateCode {
278 AuthenticityNotGuaranteed,
280 UnknownDevice,
282 UnsignedDevice,
284 UnverifiedIdentity,
286 SentInClear,
288 #[serde(alias = "PreviouslyVerified")]
290 VerificationViolation,
291 MismatchedSender,
294}
295
296#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
298pub enum AlgorithmInfo {
299 MegolmV1AesSha2 {
301 curve25519_key: String,
304 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
308
309 #[serde(default, skip_serializing_if = "Option::is_none")]
312 session_id: Option<String>,
313 },
314
315 OlmV1Curve25519AesSha2 {
317 curve25519_public_key_base64: String,
319 },
320}
321
322#[derive(Clone, Debug, PartialEq, Serialize)]
324pub struct EncryptionInfo {
325 pub sender: OwnedUserId,
328 pub sender_device: Option<OwnedDeviceId>,
331 pub algorithm_info: AlgorithmInfo,
333 pub verification_state: VerificationState,
340}
341
342impl EncryptionInfo {
343 pub fn session_id(&self) -> Option<&str> {
345 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
346 session_id.as_deref()
347 } else {
348 None
349 }
350 }
351}
352
353impl<'de> Deserialize<'de> for EncryptionInfo {
354 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
355 where
356 D: serde::Deserializer<'de>,
357 {
358 #[derive(Deserialize)]
361 struct Helper {
362 pub sender: OwnedUserId,
363 pub sender_device: Option<OwnedDeviceId>,
364 pub algorithm_info: AlgorithmInfo,
365 pub verification_state: VerificationState,
366 #[serde(rename = "session_id")]
367 pub old_session_id: Option<String>,
368 }
369
370 let Helper { sender, sender_device, algorithm_info, verification_state, old_session_id } =
371 Helper::deserialize(deserializer)?;
372
373 let algorithm_info = match algorithm_info {
374 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
375 AlgorithmInfo::MegolmV1AesSha2 {
376 session_id: session_id.or(old_session_id),
378 curve25519_key,
379 sender_claimed_keys,
380 }
381 }
382 other => other,
383 };
384
385 Ok(EncryptionInfo { sender, sender_device, algorithm_info, verification_state })
386 }
387}
388
389#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
400pub struct ThreadSummary {
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub latest_reply: Option<OwnedEventId>,
404
405 pub num_replies: u32,
411}
412
413#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
415pub enum ThreadSummaryStatus {
416 #[default]
418 Unknown,
419 None,
421 Some(ThreadSummary),
423}
424
425impl ThreadSummaryStatus {
426 fn is_unknown(&self) -> bool {
428 matches!(self, ThreadSummaryStatus::Unknown)
429 }
430
431 pub fn summary(&self) -> Option<&ThreadSummary> {
434 match self {
435 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
436 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
437 }
438 }
439}
440
441#[derive(Clone, Debug, Serialize)]
464pub struct TimelineEvent {
465 pub kind: TimelineEventKind,
467
468 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
474
475 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
480 push_actions: Option<Vec<Action>>,
481
482 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
484 pub thread_summary: ThreadSummaryStatus,
485
486 #[serde(skip)]
491 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
492}
493
494fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
496 push_actions.as_ref().is_none_or(|v| v.is_empty())
497}
498
499#[cfg(not(feature = "test-send-sync"))]
501unsafe impl Send for TimelineEvent {}
502
503#[cfg(not(feature = "test-send-sync"))]
505unsafe impl Sync for TimelineEvent {}
506
507#[cfg(feature = "test-send-sync")]
508#[test]
509fn test_send_sync_for_sync_timeline_event() {
511 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
512
513 assert_send_sync::<TimelineEvent>();
514}
515
516impl TimelineEvent {
517 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
522 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
523 }
524
525 pub fn from_plaintext_with_max_timestamp(
527 event: Raw<AnySyncTimelineEvent>,
528 max_timestamp: MilliSecondsSinceUnixEpoch,
529 ) -> Self {
530 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
531 }
532
533 pub fn from_decrypted(
535 decrypted: DecryptedRoomEvent,
536 push_actions: Option<Vec<Action>>,
537 ) -> Self {
538 Self::from_decrypted_with_max_timestamp(
539 decrypted,
540 push_actions,
541 MilliSecondsSinceUnixEpoch::now(),
542 )
543 }
544
545 pub fn from_decrypted_with_max_timestamp(
547 decrypted: DecryptedRoomEvent,
548 push_actions: Option<Vec<Action>>,
549 max_timestamp: MilliSecondsSinceUnixEpoch,
550 ) -> Self {
551 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
552 }
553
554 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
557 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
558 }
559
560 pub fn from_utd_with_max_timestamp(
562 event: Raw<AnySyncTimelineEvent>,
563 utd_info: UnableToDecryptInfo,
564 max_timestamp: MilliSecondsSinceUnixEpoch,
565 ) -> Self {
566 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
567 }
568
569 fn new(
574 kind: TimelineEventKind,
575 push_actions: Option<Vec<Action>>,
576 max_timestamp: MilliSecondsSinceUnixEpoch,
577 ) -> Self {
578 let raw = kind.raw();
579
580 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(raw);
581
582 let bundled_latest_thread_event =
583 Self::from_bundled_latest_event(&kind, latest_thread_event, max_timestamp);
584
585 let timestamp = extract_timestamp(raw, max_timestamp);
586
587 Self { kind, push_actions, timestamp, thread_summary, bundled_latest_thread_event }
588 }
589
590 pub fn to_decrypted(
598 &self,
599 decrypted: DecryptedRoomEvent,
600 push_actions: Option<Vec<Action>>,
601 ) -> Self {
602 debug_assert!(
603 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
604 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
605 );
606
607 Self {
608 kind: TimelineEventKind::Decrypted(decrypted),
609 timestamp: self.timestamp,
610 push_actions,
611 thread_summary: self.thread_summary.clone(),
612 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
613 }
614 }
615
616 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
624 debug_assert!(
625 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
626 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
627 );
628
629 Self {
630 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
631 timestamp: self.timestamp,
632 push_actions: None,
633 thread_summary: self.thread_summary.clone(),
634 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
635 }
636 }
637
638 fn from_bundled_latest_event(
642 kind: &TimelineEventKind,
643 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
644 max_timestamp: MilliSecondsSinceUnixEpoch,
645 ) -> Option<Box<Self>> {
646 let latest_event = latest_event?;
647
648 match kind {
649 TimelineEventKind::Decrypted(decrypted) => {
650 if let Some(unsigned_decryption_result) =
651 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
652 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
653 })
654 {
655 match unsigned_decryption_result {
656 UnsignedDecryptionResult::Decrypted(encryption_info) => {
657 return Some(Box::new(
660 TimelineEvent::from_decrypted_with_max_timestamp(
661 DecryptedRoomEvent {
662 event: latest_event.cast_unchecked(),
665 encryption_info: encryption_info.clone(),
666 unsigned_encryption_info: None,
671 },
672 None,
673 max_timestamp,
674 ),
675 ));
676 }
677
678 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
679 return Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
681 latest_event.cast(),
682 utd_info.clone(),
683 max_timestamp,
684 )));
685 }
686 }
687 }
688 }
689
690 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
691 }
693 }
694
695 match latest_event.get_field::<MessageLikeEventType>("type") {
696 Ok(None) => {
697 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
698 warn!(
699 ?event_id,
700 "couldn't deserialize bundled latest thread event: missing `type` field \
701 in bundled latest thread event"
702 );
703 None
704 }
705
706 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
707 Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
711 latest_event.cast(),
712 UnableToDecryptInfo {
713 session_id: None,
714 reason: UnableToDecryptReason::Unknown,
715 },
716 max_timestamp,
717 )))
718 }
719
720 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext_with_max_timestamp(
721 latest_event.cast(),
722 max_timestamp,
723 ))),
724
725 Err(err) => {
726 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
727 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
728 None
729 }
730 }
731 }
732
733 pub fn push_actions(&self) -> Option<&[Action]> {
738 self.push_actions.as_deref()
739 }
740
741 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
743 self.push_actions = Some(push_actions);
744 }
745
746 pub fn event_id(&self) -> Option<OwnedEventId> {
749 self.kind.event_id()
750 }
751
752 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
755 self.kind.raw()
756 }
757
758 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
760 match &mut self.kind {
761 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
762 TimelineEventKind::UnableToDecrypt { event, .. }
763 | TimelineEventKind::PlainText { event } => {
764 *event = replacement.cast();
767 }
768 }
769 }
770
771 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
780 self.timestamp.or_else(|| {
781 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
782
783 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
784 })
785 }
786
787 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
789 self.timestamp
790 }
791
792 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
795 self.kind.encryption_info()
796 }
797
798 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
801 self.kind.into_raw()
802 }
803}
804
805impl<'de> Deserialize<'de> for TimelineEvent {
806 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
814 where
815 D: serde::Deserializer<'de>,
816 {
817 use serde_json::{Map, Value};
818
819 let value = Map::<String, Value>::deserialize(deserializer)?;
821
822 if value.contains_key("event") {
824 let v0: SyncTimelineEventDeserializationHelperV0 =
825 serde_json::from_value(Value::Object(value)).map_err(|e| {
826 serde::de::Error::custom(format!(
827 "Unable to deserialize V0-format TimelineEvent: {e}",
828 ))
829 })?;
830 Ok(v0.into())
831 }
832 else {
834 let v1: SyncTimelineEventDeserializationHelperV1 =
835 serde_json::from_value(Value::Object(value)).map_err(|e| {
836 serde::de::Error::custom(format!(
837 "Unable to deserialize V1-format TimelineEvent: {e}",
838 ))
839 })?;
840 Ok(v1.into())
841 }
842 }
843}
844
845#[derive(Clone, Serialize, Deserialize)]
847pub enum TimelineEventKind {
848 Decrypted(DecryptedRoomEvent),
850
851 UnableToDecrypt {
853 event: Raw<AnySyncTimelineEvent>,
857
858 utd_info: UnableToDecryptInfo,
860 },
861
862 PlainText {
864 event: Raw<AnySyncTimelineEvent>,
868 },
869}
870
871impl TimelineEventKind {
872 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
875 match self {
876 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
882 TimelineEventKind::UnableToDecrypt { event, .. } => event,
883 TimelineEventKind::PlainText { event } => event,
884 }
885 }
886
887 pub fn event_id(&self) -> Option<OwnedEventId> {
890 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
891 }
892
893 pub fn is_utd(&self) -> bool {
895 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
896 }
897
898 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
901 match self {
902 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
903 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
904 }
905 }
906
907 pub fn unsigned_encryption_map(
910 &self,
911 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
912 match self {
913 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
914 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
915 }
916 }
917
918 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
921 match self {
922 TimelineEventKind::Decrypted(d) => d.event.cast(),
928 TimelineEventKind::UnableToDecrypt { event, .. } => event,
929 TimelineEventKind::PlainText { event } => event,
930 }
931 }
932
933 pub fn session_id(&self) -> Option<&str> {
936 match self {
937 TimelineEventKind::Decrypted(decrypted_room_event) => {
938 decrypted_room_event.encryption_info.session_id()
939 }
940 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
941 TimelineEventKind::PlainText { .. } => None,
942 }
943 }
944}
945
946#[cfg(not(tarpaulin_include))]
947impl fmt::Debug for TimelineEventKind {
948 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
949 match &self {
950 Self::PlainText { event } => f
951 .debug_struct("TimelineEventDecryptionResult::PlainText")
952 .field("event", &DebugRawEvent(event))
953 .finish(),
954
955 Self::UnableToDecrypt { event, utd_info } => f
956 .debug_struct("TimelineEventDecryptionResult::UnableToDecrypt")
957 .field("event", &DebugRawEvent(event))
958 .field("utd_info", &utd_info)
959 .finish(),
960
961 Self::Decrypted(decrypted) => {
962 f.debug_tuple("TimelineEventDecryptionResult::Decrypted").field(decrypted).finish()
963 }
964 }
965 }
966}
967
968#[derive(Clone, Serialize, Deserialize)]
969pub struct DecryptedRoomEvent {
971 pub event: Raw<AnyTimelineEvent>,
979
980 pub encryption_info: Arc<EncryptionInfo>,
982
983 #[serde(skip_serializing_if = "Option::is_none")]
988 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
989}
990
991#[cfg(not(tarpaulin_include))]
992impl fmt::Debug for DecryptedRoomEvent {
993 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
995
996 f.debug_struct("DecryptedRoomEvent")
997 .field("event", &DebugRawEvent(event))
998 .field("encryption_info", encryption_info)
999 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1000 .finish()
1001 }
1002}
1003
1004#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1006pub enum UnsignedEventLocation {
1007 RelationsReplace,
1010 RelationsThreadLatestEvent,
1013}
1014
1015impl UnsignedEventLocation {
1016 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1023 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1024
1025 match self {
1026 Self::RelationsReplace => relations.get_mut("m.replace"),
1027 Self::RelationsThreadLatestEvent => {
1028 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1029 }
1030 }
1031 }
1032}
1033
1034#[derive(Debug, Clone, Serialize, Deserialize)]
1036pub enum UnsignedDecryptionResult {
1037 Decrypted(Arc<EncryptionInfo>),
1039 UnableToDecrypt(UnableToDecryptInfo),
1041}
1042
1043impl UnsignedDecryptionResult {
1044 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1047 match self {
1048 Self::Decrypted(info) => Some(info),
1049 Self::UnableToDecrypt(_) => None,
1050 }
1051 }
1052}
1053
1054#[derive(Debug, Clone, Serialize, Deserialize)]
1056pub struct UnableToDecryptInfo {
1057 #[serde(skip_serializing_if = "Option::is_none")]
1060 pub session_id: Option<String>,
1061
1062 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1064 pub reason: UnableToDecryptReason,
1065}
1066
1067fn unknown_utd_reason() -> UnableToDecryptReason {
1068 UnableToDecryptReason::Unknown
1069}
1070
1071pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1074where
1075 D: serde::Deserializer<'de>,
1076{
1077 let v: serde_json::Value = Deserialize::deserialize(d)?;
1079 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1082 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1083 }
1084 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1087}
1088
1089#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1091pub enum UnableToDecryptReason {
1092 #[doc(hidden)]
1095 Unknown,
1096
1097 MalformedEncryptedEvent,
1101
1102 MissingMegolmSession {
1105 withheld_code: Option<WithheldCode>,
1108 },
1109
1110 UnknownMegolmMessageIndex,
1113
1114 MegolmDecryptionFailure,
1121
1122 PayloadDeserializationFailure,
1124
1125 MismatchedIdentityKeys,
1129
1130 SenderIdentityNotTrusted(VerificationLevel),
1134
1135 #[cfg(feature = "experimental-encrypted-state-events")]
1138 StateKeyVerificationFailed,
1139}
1140
1141impl UnableToDecryptReason {
1142 pub fn is_missing_room_key(&self) -> bool {
1145 matches!(
1148 self,
1149 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1150 )
1151 }
1152}
1153
1154#[derive(
1158 Clone,
1159 PartialEq,
1160 Eq,
1161 Hash,
1162 AsStrAsRefStr,
1163 AsRefStr,
1164 FromString,
1165 DebugAsRefStr,
1166 SerializeAsRefStr,
1167 DeserializeFromCowStr,
1168)]
1169pub enum WithheldCode {
1170 #[ruma_enum(rename = "m.blacklisted")]
1172 Blacklisted,
1173
1174 #[ruma_enum(rename = "m.unverified")]
1176 Unverified,
1177
1178 #[ruma_enum(rename = "m.unauthorised")]
1182 Unauthorised,
1183
1184 #[ruma_enum(rename = "m.unavailable")]
1187 Unavailable,
1188
1189 #[ruma_enum(rename = "m.no_olm")]
1193 NoOlm,
1194
1195 #[doc(hidden)]
1196 _Custom(PrivOwnedStr),
1197}
1198
1199impl fmt::Display for WithheldCode {
1200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1201 let string = match self {
1202 WithheldCode::Blacklisted => "The sender has blocked you.",
1203 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1204 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1205 WithheldCode::Unavailable => "The requested key was not found.",
1206 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1207 _ => self.as_str(),
1208 };
1209
1210 f.write_str(string)
1211 }
1212}
1213
1214#[doc(hidden)]
1218#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1219pub struct PrivOwnedStr(pub Box<str>);
1220
1221#[cfg(not(tarpaulin_include))]
1222impl fmt::Debug for PrivOwnedStr {
1223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1224 self.0.fmt(f)
1225 }
1226}
1227
1228#[derive(Debug, Deserialize)]
1233struct SyncTimelineEventDeserializationHelperV1 {
1234 kind: TimelineEventKind,
1236
1237 #[serde(default)]
1240 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1241
1242 #[serde(default)]
1244 push_actions: Vec<Action>,
1245
1246 #[serde(default)]
1248 thread_summary: ThreadSummaryStatus,
1249}
1250
1251impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1252 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1253 let SyncTimelineEventDeserializationHelperV1 {
1254 kind,
1255 timestamp,
1256 push_actions,
1257 thread_summary,
1258 } = value;
1259
1260 TimelineEvent {
1269 kind,
1270 timestamp,
1271 push_actions: Some(push_actions),
1272 thread_summary,
1273 bundled_latest_thread_event: None,
1275 }
1276 }
1277}
1278
1279#[derive(Deserialize)]
1281struct SyncTimelineEventDeserializationHelperV0 {
1282 event: Raw<AnySyncTimelineEvent>,
1284
1285 encryption_info: Option<Arc<EncryptionInfo>>,
1289
1290 #[serde(default)]
1292 push_actions: Vec<Action>,
1293
1294 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1299}
1300
1301impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1302 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1303 let SyncTimelineEventDeserializationHelperV0 {
1304 event,
1305 encryption_info,
1306 push_actions,
1307 unsigned_encryption_info,
1308 } = value;
1309
1310 let timestamp = None;
1317
1318 let kind = match encryption_info {
1319 Some(encryption_info) => {
1320 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1321 event: event.cast_unchecked(),
1328 encryption_info,
1329 unsigned_encryption_info,
1330 })
1331 }
1332
1333 None => TimelineEventKind::PlainText { event },
1334 };
1335
1336 TimelineEvent {
1337 kind,
1338 timestamp,
1339 push_actions: Some(push_actions),
1340 thread_summary: ThreadSummaryStatus::Unknown,
1342 bundled_latest_thread_event: None,
1344 }
1345 }
1346}
1347
1348#[derive(Debug, Clone, PartialEq)]
1350pub enum ToDeviceUnableToDecryptReason {
1351 DecryptionFailure,
1354
1355 UnverifiedSenderDevice,
1359
1360 NoOlmMachine,
1363
1364 EncryptionIsDisabled,
1366}
1367
1368#[derive(Clone, Debug)]
1370pub struct ToDeviceUnableToDecryptInfo {
1371 pub reason: ToDeviceUnableToDecryptReason,
1373}
1374
1375#[derive(Clone, Debug)]
1377pub enum ProcessedToDeviceEvent {
1378 Decrypted {
1381 raw: Raw<AnyToDeviceEvent>,
1383 encryption_info: EncryptionInfo,
1385 },
1386
1387 UnableToDecrypt {
1389 encrypted_event: Raw<AnyToDeviceEvent>,
1390 utd_info: ToDeviceUnableToDecryptInfo,
1391 },
1392
1393 PlainText(Raw<AnyToDeviceEvent>),
1395
1396 Invalid(Raw<AnyToDeviceEvent>),
1400}
1401
1402impl ProcessedToDeviceEvent {
1403 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1406 match self {
1407 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1408 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1409 encrypted_event.clone()
1410 }
1411 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1412 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1413 }
1414 }
1415
1416 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1418 match self {
1419 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1420 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1421 ProcessedToDeviceEvent::PlainText(event) => event,
1422 ProcessedToDeviceEvent::Invalid(event) => event,
1423 }
1424 }
1425}
1426
1427#[cfg(test)]
1428mod tests {
1429 use std::{collections::BTreeMap, sync::Arc};
1430
1431 use assert_matches::assert_matches;
1432 use assert_matches2::assert_let;
1433 use insta::{assert_json_snapshot, with_settings};
1434 use ruma::{
1435 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1436 events::room::message::RoomMessageEventContent, serde::Raw, user_id,
1437 };
1438 use serde::Deserialize;
1439 use serde_json::json;
1440
1441 use super::{
1442 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1443 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1444 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1445 VerificationState, WithheldCode,
1446 };
1447 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1448
1449 fn example_event() -> serde_json::Value {
1450 json!({
1451 "content": RoomMessageEventContent::text_plain("secret"),
1452 "type": "m.room.message",
1453 "event_id": "$xxxxx:example.org",
1454 "room_id": "!someroom:example.com",
1455 "origin_server_ts": 2189,
1456 "sender": "@carl:example.com",
1457 })
1458 }
1459
1460 #[test]
1461 fn sync_timeline_debug_content() {
1462 let room_event =
1463 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1464 let debug_s = format!("{room_event:?}");
1465 assert!(
1466 !debug_s.contains("secret"),
1467 "Debug representation contains event content!\n{debug_s}"
1468 );
1469 }
1470
1471 #[test]
1472 fn old_verification_state_to_new_migration() {
1473 #[derive(Deserialize)]
1474 struct State {
1475 state: VerificationState,
1476 }
1477
1478 let state = json!({
1479 "state": "Trusted",
1480 });
1481 let deserialized: State =
1482 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1483 assert_eq!(deserialized.state, VerificationState::Verified);
1484
1485 let state = json!({
1486 "state": "UnknownDevice",
1487 });
1488
1489 let deserialized: State =
1490 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1491
1492 assert_eq!(
1493 deserialized.state,
1494 VerificationState::Unverified(VerificationLevel::None(
1495 DeviceLinkProblem::MissingDevice
1496 ))
1497 );
1498
1499 let state = json!({
1500 "state": "Untrusted",
1501 });
1502 let deserialized: State =
1503 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1504
1505 assert_eq!(
1506 deserialized.state,
1507 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1508 );
1509 }
1510
1511 #[test]
1512 fn test_verification_level_deserializes() {
1513 #[derive(Deserialize)]
1515 struct Container {
1516 verification_level: VerificationLevel,
1517 }
1518 let container = json!({ "verification_level": "VerificationViolation" });
1519
1520 let deserialized: Container = serde_json::from_value(container)
1522 .expect("We can deserialize the old PreviouslyVerified value");
1523
1524 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1526 }
1527
1528 #[test]
1529 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1530 #[derive(Deserialize)]
1532 struct Container {
1533 verification_level: VerificationLevel,
1534 }
1535 let container = json!({ "verification_level": "PreviouslyVerified" });
1536
1537 let deserialized: Container = serde_json::from_value(container)
1539 .expect("We can deserialize the old PreviouslyVerified value");
1540
1541 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1543 }
1544
1545 #[test]
1546 fn test_shield_state_code_deserializes() {
1547 #[derive(Deserialize)]
1549 struct Container {
1550 shield_state_code: ShieldStateCode,
1551 }
1552 let container = json!({ "shield_state_code": "VerificationViolation" });
1553
1554 let deserialized: Container = serde_json::from_value(container)
1556 .expect("We can deserialize the old PreviouslyVerified value");
1557
1558 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1560 }
1561
1562 #[test]
1563 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1564 #[derive(Deserialize)]
1566 struct Container {
1567 shield_state_code: ShieldStateCode,
1568 }
1569 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1570
1571 let deserialized: Container = serde_json::from_value(container)
1573 .expect("We can deserialize the old PreviouslyVerified value");
1574
1575 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1577 }
1578
1579 #[test]
1580 fn sync_timeline_event_serialisation() {
1581 let room_event = TimelineEvent {
1582 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1583 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1584 encryption_info: Arc::new(EncryptionInfo {
1585 sender: user_id!("@sender:example.com").to_owned(),
1586 sender_device: None,
1587 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1588 curve25519_key: "xxx".to_owned(),
1589 sender_claimed_keys: Default::default(),
1590 session_id: Some("xyz".to_owned()),
1591 },
1592 verification_state: VerificationState::Verified,
1593 }),
1594 unsigned_encryption_info: Some(BTreeMap::from([(
1595 UnsignedEventLocation::RelationsReplace,
1596 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1597 session_id: Some("xyz".to_owned()),
1598 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1599 }),
1600 )])),
1601 }),
1602 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1603 push_actions: Default::default(),
1604 thread_summary: ThreadSummaryStatus::Unknown,
1605 bundled_latest_thread_event: None,
1606 };
1607
1608 let serialized = serde_json::to_value(&room_event).unwrap();
1609
1610 assert_eq!(
1612 serialized,
1613 json!({
1614 "kind": {
1615 "Decrypted": {
1616 "event": {
1617 "content": {"body": "secret", "msgtype": "m.text"},
1618 "event_id": "$xxxxx:example.org",
1619 "origin_server_ts": 2189,
1620 "room_id": "!someroom:example.com",
1621 "sender": "@carl:example.com",
1622 "type": "m.room.message",
1623 },
1624 "encryption_info": {
1625 "sender": "@sender:example.com",
1626 "sender_device": null,
1627 "algorithm_info": {
1628 "MegolmV1AesSha2": {
1629 "curve25519_key": "xxx",
1630 "sender_claimed_keys": {},
1631 "session_id": "xyz",
1632 }
1633 },
1634 "verification_state": "Verified",
1635 },
1636 "unsigned_encryption_info": {
1637 "RelationsReplace": {"UnableToDecrypt": {
1638 "session_id": "xyz",
1639 "reason": "MalformedEncryptedEvent",
1640 }}
1641 }
1642 }
1643 },
1644 "timestamp": 2189,
1645 })
1646 );
1647
1648 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1650 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1651 assert_matches!(
1652 event.encryption_info().unwrap().algorithm_info,
1653 AlgorithmInfo::MegolmV1AesSha2 { .. }
1654 );
1655 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1656 assert_eq!(event.timestamp(), event.timestamp_raw());
1657
1658 let serialized = json!({
1660 "event": {
1661 "content": {"body": "secret", "msgtype": "m.text"},
1662 "event_id": "$xxxxx:example.org",
1663 "origin_server_ts": 2189,
1664 "room_id": "!someroom:example.com",
1665 "sender": "@carl:example.com",
1666 "type": "m.room.message",
1667 },
1668 "encryption_info": {
1669 "sender": "@sender:example.com",
1670 "sender_device": null,
1671 "algorithm_info": {
1672 "MegolmV1AesSha2": {
1673 "curve25519_key": "xxx",
1674 "sender_claimed_keys": {}
1675 }
1676 },
1677 "verification_state": "Verified",
1678 },
1679 });
1680 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1681 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1682 assert_matches!(
1683 event.encryption_info().unwrap().algorithm_info,
1684 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1685 );
1686 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1687 assert!(event.timestamp_raw().is_none());
1688
1689 let serialized = json!({
1692 "event": {
1693 "content": {"body": "secret", "msgtype": "m.text"},
1694 "event_id": "$xxxxx:example.org",
1695 "origin_server_ts": 2189,
1696 "room_id": "!someroom:example.com",
1697 "sender": "@carl:example.com",
1698 "type": "m.room.message",
1699 },
1700 "encryption_info": {
1701 "sender": "@sender:example.com",
1702 "sender_device": null,
1703 "algorithm_info": {
1704 "MegolmV1AesSha2": {
1705 "curve25519_key": "xxx",
1706 "sender_claimed_keys": {}
1707 }
1708 },
1709 "verification_state": "Verified",
1710 },
1711 "unsigned_encryption_info": {
1712 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1713 }
1714 });
1715 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1716 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1717 assert_matches!(
1718 event.encryption_info().unwrap().algorithm_info,
1719 AlgorithmInfo::MegolmV1AesSha2 { .. }
1720 );
1721 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1722 assert!(event.timestamp_raw().is_none());
1723 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1724 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1725 assert_eq!(map.len(), 1);
1726 let (location, result) = map.into_iter().next().unwrap();
1727 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1728 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1729 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1730 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1731 })
1732 });
1733 });
1734 }
1735
1736 #[test]
1737 fn test_creating_or_deserializing_an_event_extracts_summary() {
1738 let event = json!({
1739 "event_id": "$eid:example.com",
1740 "type": "m.room.message",
1741 "sender": "@alice:example.com",
1742 "origin_server_ts": 42,
1743 "content": {
1744 "body": "Hello, world!",
1745 },
1746 "unsigned": {
1747 "m.relations": {
1748 "m.thread": {
1749 "latest_event": {
1750 "event_id": "$latest_event:example.com",
1751 "type": "m.room.message",
1752 "sender": "@bob:example.com",
1753 "origin_server_ts": 42,
1754 "content": {
1755 "body": "Hello to you too!",
1756 "msgtype": "m.text",
1757 }
1758 },
1759 "count": 2,
1760 "current_user_participated": true,
1761 }
1762 }
1763 }
1764 });
1765
1766 let raw = Raw::new(&event).unwrap().cast_unchecked();
1767
1768 let timeline_event = TimelineEvent::from_plaintext(raw);
1771 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1772 assert_eq!(num_replies, 2);
1773 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1774 });
1775
1776 assert!(timeline_event.bundled_latest_thread_event.is_some());
1777
1778 let serialized_timeline_item = json!({
1781 "kind": {
1782 "PlainText": {
1783 "event": event
1784 }
1785 }
1786 });
1787
1788 let timeline_event: TimelineEvent =
1789 serde_json::from_value(serialized_timeline_item).unwrap();
1790 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1791
1792 assert!(timeline_event.bundled_latest_thread_event.is_none());
1795 }
1796
1797 #[test]
1798 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1799 let serialized = json!({
1816 "kind": {
1817 "UnableToDecrypt": {
1818 "event": {
1819 "content": {
1820 "algorithm": "m.megolm.v1.aes-sha2",
1821 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1822 "device_id": "SKCGPNUWAU",
1823 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1824 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1825 },
1826 "event_id": "$xxxxx:example.org",
1827 "origin_server_ts": 2189,
1828 "room_id": "!someroom:example.com",
1829 "sender": "@carl:example.com",
1830 "type": "m.room.message"
1831 },
1832 "utd_info": {
1833 "reason": "MissingMegolmSession",
1834 "session_id": "session000"
1835 }
1836 }
1837 }
1838 });
1839
1840 let result = serde_json::from_value(serialized);
1841 assert!(result.is_ok());
1842
1843 let event: TimelineEvent = result.unwrap();
1845 assert_matches!(
1846 event.kind,
1847 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1848 assert_matches!(
1849 utd_info.reason,
1850 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1851 );
1852 }
1853 )
1854 }
1855
1856 #[test]
1857 fn unable_to_decrypt_info_migration_for_withheld() {
1858 let old_format = json!({
1859 "reason": "MissingMegolmSession",
1860 "session_id": "session000"
1861 });
1862
1863 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1864 let session_id = Some("session000".to_owned());
1865
1866 assert_eq!(deserialized.session_id, session_id);
1867 assert_eq!(
1868 deserialized.reason,
1869 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1870 );
1871
1872 let new_format = json!({
1873 "session_id": "session000",
1874 "reason": {
1875 "MissingMegolmSession": {
1876 "withheld_code": null
1877 }
1878 }
1879 });
1880
1881 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1882
1883 assert_eq!(
1884 deserialized.reason,
1885 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1886 );
1887 assert_eq!(deserialized.session_id, session_id);
1888 }
1889
1890 #[test]
1891 fn unable_to_decrypt_reason_is_missing_room_key() {
1892 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1893 assert!(reason.is_missing_room_key());
1894
1895 let reason = UnableToDecryptReason::MissingMegolmSession {
1896 withheld_code: Some(WithheldCode::Blacklisted),
1897 };
1898 assert!(!reason.is_missing_room_key());
1899
1900 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1901 assert!(reason.is_missing_room_key());
1902 }
1903
1904 #[test]
1905 fn snapshot_test_verification_level() {
1906 with_settings!({ prepend_module_to_snapshot => false }, {
1907 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1908 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1909 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1910 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1911 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1912 });
1913 }
1914
1915 #[test]
1916 fn snapshot_test_verification_states() {
1917 with_settings!({ prepend_module_to_snapshot => false }, {
1918 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1919 assert_json_snapshot!(VerificationState::Unverified(
1920 VerificationLevel::VerificationViolation
1921 ));
1922 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1923 DeviceLinkProblem::InsecureSource,
1924 )));
1925 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1926 DeviceLinkProblem::MissingDevice,
1927 )));
1928 assert_json_snapshot!(VerificationState::Verified);
1929 });
1930 }
1931
1932 #[test]
1933 fn snapshot_test_shield_states() {
1934 with_settings!({ prepend_module_to_snapshot => false }, {
1935 assert_json_snapshot!(ShieldState::None);
1936 assert_json_snapshot!(ShieldState::Red {
1937 code: ShieldStateCode::UnverifiedIdentity,
1938 message: "a message"
1939 });
1940 assert_json_snapshot!(ShieldState::Grey {
1941 code: ShieldStateCode::AuthenticityNotGuaranteed,
1942 message: "authenticity of this message cannot be guaranteed",
1943 });
1944 });
1945 }
1946
1947 #[test]
1948 fn snapshot_test_shield_codes() {
1949 with_settings!({ prepend_module_to_snapshot => false }, {
1950 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1951 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1952 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1953 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1954 assert_json_snapshot!(ShieldStateCode::SentInClear);
1955 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1956 });
1957 }
1958
1959 #[test]
1960 fn snapshot_test_algorithm_info() {
1961 let mut map = BTreeMap::new();
1962 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1963 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1964 let info = AlgorithmInfo::MegolmV1AesSha2 {
1965 curve25519_key: "curvecurvecurve".into(),
1966 sender_claimed_keys: BTreeMap::from([
1967 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1968 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1969 ]),
1970 session_id: None,
1971 };
1972
1973 with_settings!({ prepend_module_to_snapshot => false }, {
1974 assert_json_snapshot!(info)
1975 });
1976 }
1977
1978 #[test]
1979 fn test_encryption_info_migration() {
1980 let old_format = json!({
1983 "sender": "@alice:localhost",
1984 "sender_device": "ABCDEFGH",
1985 "algorithm_info": {
1986 "MegolmV1AesSha2": {
1987 "curve25519_key": "curvecurvecurve",
1988 "sender_claimed_keys": {}
1989 }
1990 },
1991 "verification_state": "Verified",
1992 "session_id": "mysessionid76"
1993 });
1994
1995 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
1996 let expected_session_id = Some("mysessionid76".to_owned());
1997
1998 assert_let!(
1999 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2000 );
2001 assert_eq!(session_id, expected_session_id);
2002
2003 assert_json_snapshot!(deserialized);
2004 }
2005
2006 #[test]
2007 fn snapshot_test_encryption_info() {
2008 let info = EncryptionInfo {
2009 sender: user_id!("@alice:localhost").to_owned(),
2010 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
2011 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2012 curve25519_key: "curvecurvecurve".into(),
2013 sender_claimed_keys: Default::default(),
2014 session_id: Some("mysessionid76".to_owned()),
2015 },
2016 verification_state: VerificationState::Verified,
2017 };
2018
2019 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2020 assert_json_snapshot!(info)
2021 })
2022 }
2023
2024 #[test]
2025 fn snapshot_test_sync_timeline_event() {
2026 let room_event = TimelineEvent {
2027 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2028 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2029 encryption_info: Arc::new(EncryptionInfo {
2030 sender: user_id!("@sender:example.com").to_owned(),
2031 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
2032 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2033 curve25519_key: "xxx".to_owned(),
2034 sender_claimed_keys: BTreeMap::from([
2035 (
2036 DeviceKeyAlgorithm::Ed25519,
2037 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2038 ),
2039 (
2040 DeviceKeyAlgorithm::Curve25519,
2041 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2042 ),
2043 ]),
2044 session_id: Some("mysessionid112".to_owned()),
2045 },
2046 verification_state: VerificationState::Verified,
2047 }),
2048 unsigned_encryption_info: Some(BTreeMap::from([(
2049 UnsignedEventLocation::RelationsThreadLatestEvent,
2050 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2051 session_id: Some("xyz".to_owned()),
2052 reason: UnableToDecryptReason::MissingMegolmSession {
2053 withheld_code: Some(WithheldCode::Unverified),
2054 },
2055 }),
2056 )])),
2057 }),
2058 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2059 push_actions: Default::default(),
2060 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2061 num_replies: 2,
2062 latest_reply: None,
2063 }),
2064 bundled_latest_thread_event: None,
2065 };
2066
2067 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2068 assert_json_snapshot! {
2071 serde_json::to_value(&room_event).unwrap(),
2072 }
2073 });
2074 }
2075}